/******************************************************************************* * Copyright (c) 2011, 2015 Ericsson, Ecole Polytechnique de Montreal and others * * All rights reserved. This program and the accompanying materials are made * available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: Matthew Khouzam - Initial Design and Grammar * Contributors: Simon Marchi - Initial API and implementation *******************************************************************************/ package org.eclipse.tracecompass.ctf.core.event.metadata; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.ctf.core.event.types.EnumDeclaration; import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration; import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration; import org.eclipse.tracecompass.ctf.core.event.types.VariantDeclaration; import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException; /** * <b><u>DeclarationScope</u></b> * <p> * A DeclarationScope keeps track of the various CTF declarations for a given * scope. * * TODO: The notion of "symbols" and the notion of "scope" are misused in this * parser, which leads to inefficient tree management. It should be cleaned up. * * @author Matthew Khouzam * @author Simon Marchi * @since 1.1 * */ public class DeclarationScope { // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ private DeclarationScope fParentScope; private @NonNull Map<String, DeclarationScope> fChildren = new HashMap<>(); private final Map<String, StructDeclaration> fStructs = new HashMap<>(); private final Map<String, EnumDeclaration> fEnums = new HashMap<>(); private final Map<String, VariantDeclaration> fVariants = new HashMap<>(); private final Map<String, IDeclaration> fTypes = new HashMap<>(); private final Map<String, IDeclaration> fIdentifiers = new HashMap<>(); private String fName; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Creates a declaration scope with no parent. */ public DeclarationScope() { } /** * Creates a declaration scope with the specified parent. * * @param parentScope * The parent of the newly created scope. * @param name * scope name */ public DeclarationScope(DeclarationScope parentScope, String name) { fParentScope = parentScope; fName = name; if (parentScope != null) { parentScope.registerChild(name, this); } } private void registerChild(String name, DeclarationScope declarationScope) { fChildren.put(name, declarationScope); } // ------------------------------------------------------------------------ // Getters/Setters/Predicates // ------------------------------------------------------------------------ /** * Returns the parent of the current scope. * * @return The parent scope. */ public DeclarationScope getParentScope() { return fParentScope; } /** * Sets the name of the scope * * @param name * the name */ public void setName(String name) { if (hasParent()) { fParentScope.fChildren.remove(fName); fParentScope.fChildren.put(name, this); fName = name; } } // ------------------------------------------------------------------------ // Registration operations // ------------------------------------------------------------------------ /** * Registers a type declaration. * * @param name * The name of the type. * @param declaration * The type declaration. * @throws ParseException * if a type with the same name has already been defined. */ public void registerType(String name, IDeclaration declaration) throws ParseException { /* Check if the type has been defined in the current scope */ if (fTypes.containsKey(name)) { throw new ParseException("Type has already been defined:" + name); //$NON-NLS-1$ } /* Add it to the register. */ fTypes.put(name, declaration); } /** * Registers an identifier declaration. * * @param name * name of the identifier * @param declaration * the identfier's declaration * @throws ParseException * if an identifier with the same name has already been defined. */ public void registerIdentifier(String name, IDeclaration declaration) throws ParseException { /* Check if the type has been defined in the current scope */ if (fIdentifiers.containsKey(name)) { throw new ParseException("Identifier has already been defined:" + name); //$NON-NLS-1$ } /* Add it to the register. */ fIdentifiers.put(name, declaration); } /** * Registers a struct declaration. * * @param name * The name of the struct. * @param declaration * The declaration of the struct. * @throws ParseException * if a struct with the same name has already been registered. */ public void registerStruct(String name, StructDeclaration declaration) throws ParseException { /* Check if the struct has been defined in the current scope. */ if (fStructs.containsKey(name)) { throw new ParseException("Struct has already been defined:" + name); //$NON-NLS-1$ } /* Add it to the register. */ fStructs.put(name, declaration); /* It also defined a new type, so add it to the type declarations. */ String structPrefix = "struct "; //$NON-NLS-1$ registerType(structPrefix + name, declaration); } /** * Registers an enum declaration. * * @param name * The name of the enum. * @param declaration * The declaration of the enum. * @throws ParseException * if an enum with the same name has already been registered. */ public void registerEnum(String name, EnumDeclaration declaration) throws ParseException { /* Check if the enum has been defined in the current scope. */ if (lookupEnum(name) != null) { throw new ParseException("Enum has already been defined:" + name); //$NON-NLS-1$ } /* Add it to the register. */ fEnums.put(name, declaration); /* It also defined a new type, so add it to the type declarations. */ String enumPrefix = "enum "; //$NON-NLS-1$ registerType(enumPrefix + name, declaration); } /** * Registers a variant declaration. * * @param name * The name of the variant. * @param declaration * The declaration of the variant. * @throws ParseException * if a variant with the same name has already been registered. */ public void registerVariant(String name, VariantDeclaration declaration) throws ParseException { /* Check if the variant has been defined in the current scope. */ final VariantDeclaration lookupVariant = lookupVariant(name); if (declaration.equals(lookupVariant)) { return; } if (lookupVariant != null) { throw new ParseException("Variant has already been defined:" + name); //$NON-NLS-1$ } /* Add it to the register. */ fVariants.put(name, declaration); /* It also defined a new type, so add it to the type declarations. */ String variantPrefix = "variant "; //$NON-NLS-1$ registerType(variantPrefix + name, declaration); } // ------------------------------------------------------------------------ // Lookup operations // ------------------------------------------------------------------------ /** * Lookup the children scopes of this scope * * @param name * the child to lookup * @return the scope or null */ public @Nullable DeclarationScope lookupChild(String name) { return fChildren.get(name); } /** * Lookup the children scopes of this scope and this scope's parents * * @param name * the child to lookup * @return the scope or null */ public @Nullable DeclarationScope lookupChildRecursive(String name) { final DeclarationScope declarationScope = fChildren.get(name); if (declarationScope == null && hasParent()) { return fParentScope.lookupChildRecursive(name); } return declarationScope; } /** * Looks up a type declaration in the current scope. * * @param name * The name of the type to search for. * @return The type declaration, or null if no type with that name has been * defined. */ public IDeclaration lookupType(String name) { return fTypes.get(name); } /** * Looks up a type declaration in the current scope and recursively in the * parent scopes. * * @param name * The name of the type to search for. * @return The type declaration, or null if no type with that name has been * defined. */ public IDeclaration lookupTypeRecursive(String name) { IDeclaration declaration = lookupType(name); if (declaration != null) { return declaration; } else if (hasParent()) { return fParentScope.lookupTypeRecursive(name); } else { return null; } } /** * Looks up a struct declaration. * * @param name * The name of the struct to search for. * @return The struct declaration, or null if no struct with that name has * been defined. */ public StructDeclaration lookupStruct(String name) { return fStructs.get(name); } /** * Looks up a struct declaration in the current scope and recursively in the * parent scopes. * * @param name * The name of the struct to search for. * @return The struct declaration, or null if no struct with that name has * been defined. */ public StructDeclaration lookupStructRecursive(String name) { StructDeclaration declaration = lookupStruct(name); if (declaration != null) { return declaration; } else if (hasParent()) { return fParentScope.lookupStructRecursive(name); } else { return null; } } /** * Looks up an enum declaration. * * @param name * The name of the enum to search for. * @return The enum declaration, or null if no enum with that name has been * defined. */ public EnumDeclaration lookupEnum(String name) { return fEnums.get(name); } /** * Looks up an enum declaration in the current scope and recursively in the * parent scopes. * * @param name * The name of the enum to search for. * @return The enum declaration, or null if no enum with that name has been * defined. */ public EnumDeclaration lookupEnumRecursive(String name) { EnumDeclaration declaration = lookupEnum(name); if (declaration != null) { return declaration; } else if (hasParent()) { return fParentScope.lookupEnumRecursive(name); } else { return null; } } /** * Looks up a variant declaration. * * @param name * The name of the variant to search for. * @return The variant declaration, or null if no variant with that name has * been defined. */ public VariantDeclaration lookupVariant(String name) { return fVariants.get(name); } /** * Looks up a variant declaration in the current scope and recursively in * the parent scopes. * * @param name * The name of the variant to search for. * @return The variant declaration, or null if no variant with that name has * been defined. */ public VariantDeclaration lookupVariantRecursive(String name) { VariantDeclaration declaration = lookupVariant(name); if (declaration != null) { return declaration; } else if (hasParent()) { return fParentScope.lookupVariantRecursive(name); } else { return null; } } private boolean hasParent() { return fParentScope != null; } /** * Lookup query for an identifier in this scope. * * @param identifier * the name of the identifier to search for. In the case of int * x; it would be "x" * @return the declaration of the type associated to that identifier */ public IDeclaration lookupIdentifier(String identifier) { return fIdentifiers.get(identifier); } /** * Lookup query for an identifier through this scope and its ancestors. An * ancestor scope is a scope in which this scope is nested. * * @param identifier * the name of the identifier to search for. In the case of int * x; it would be "x" * @return the declaration of the type associated to that identifier */ public IDeclaration lookupIdentifierRecursive(String identifier) { if (identifier.contains(".")) { //$NON-NLS-1$ String[] scopes = identifier.split("\\."); //$NON-NLS-1$ return lookupIdentifierRecursive(scopes); } IDeclaration declaration = lookupIdentifier(identifier); if (declaration != null) { return declaration; } else if (hasParent()) { return fParentScope.lookupIdentifierRecursive(identifier); } return null; } private IDeclaration lookupIdentifierRecursive(String[] scopes) { if (scopes.length < 1) { return null; } // find first element DeclarationScope scope = this; scope = lookupRoot(scopes, scope); if (scope == null) { return null; } for (int i = 1; i < scopes.length; i++) { final String scopeName = scopes[i]; if (scope == null) { return null; } IDeclaration declaration = lookupIdentifierElement(scope, scopeName, Arrays.copyOfRange(scopes, i, scopes.length)); if (declaration != null) { return declaration; } scope = scope.fChildren.get(scopeName); } return null; } private static DeclarationScope lookupRoot(String[] scopes, DeclarationScope originScope) { DeclarationScope scope = originScope; final String rootElement = scopes[0]; while (!rootElement.equals(scope.fName)) { if (!scope.hasParent()) { return scope.fChildren.get(rootElement); } scope = scope.getParentScope(); } return scope; } private IDeclaration lookupIdentifierElement(DeclarationScope scope, String scopeName, String[] scopes) { if (scope.fStructs.containsKey(scopeName)) { return lookupStructScope(scope, scopeName, scopes); } else if (scope.fTypes.containsKey(scopeName)) { return scope.fTypes.get(scopeName); } else if (scope.fEnums.containsKey(scopeName)) { return scope.fEnums.get(scopeName); } else if (scope.fIdentifiers.containsKey(scopeName)) { return scope.fIdentifiers.get(scopeName); } return null; } private IDeclaration lookupStructScope(DeclarationScope scope, String scopeName, String[] scopes) { final IDeclaration structDeclaration = scope.fStructs.get(scopeName); if (scopes.length <= 1) { return structDeclaration; } return lookupIdentifierStructElement((StructDeclaration) structDeclaration, scopes[1], Arrays.copyOfRange(scopes, 2, scopes.length)); } private IDeclaration lookupIdentifierStructElement(StructDeclaration structDeclaration, String string, String[] children) { IDeclaration field = structDeclaration.getField(string); if (children == null || children.length <= 0) { return field; } if (field instanceof StructDeclaration) { StructDeclaration fieldStructDeclaration = (StructDeclaration) field; return lookupIdentifierStructElement(fieldStructDeclaration, children[0], Arrays.copyOfRange(children, 1, children.length)); } return null; } /** * Get all the type names of this scope. * * @return The type names */ public Set<String> getTypeNames() { return fTypes.keySet(); } /** * Replace a type with a new one. * * @param name * The name of the type * @param newType * The type * @throws ParseException * If the type does not exist. */ public void replaceType(String name, IDeclaration newType) throws ParseException { if (fTypes.containsKey(name)) { fTypes.put(name, newType); } else { throw new ParseException("Trace does not contain type:" + name); //$NON-NLS-1$ } } /** * Add a child scope * * @param scope * the child */ public void addChild(DeclarationScope scope) { final DeclarationScope oldParent = scope.getParentScope(); if (oldParent != null) { oldParent.fChildren.remove(scope.fName); } fChildren.put(scope.fName, scope); scope.fParentScope = this; } @Override public String toString() { return "Scope : " + fName + " children: " + fChildren.size() + (fParentScope == null ? "" : (" parent " + fParentScope.fName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } }