Class SymbolsAndScopes

java.lang.Object
org.ek9lang.compiler.common.SymbolsAndScopes

public class SymbolsAndScopes extends Object
Used as external helper to record symbols and scopes across the parser. They are held in the parsedModule against specific nodes in the parseTree. This enables later phases to look up the symbols and scopes when it encounters the same node in the parseTree. In this way the symbols get 'fleshed out' with more details in each of the compiler phases. This does push scopes on to the scope stack - that the listener uses. IMPORTANT concepts here: 1. The ParsedModule keeps a record of a Symbol against a ParseTree node 2. The ParsedModule keeps a record of a Scope against a ParseTree node 3. The scopeStack keeps a dynamic position of the current scope stuff is being added to. But the scopeStack is ephemeral - it grows and shrinks based on the language constructs the listener encounters, as stuff is added in - that scope become richer. But when the end of the scope is encountered - it is popped off the scopeStack. All would be lost! - But for the fact that the symbols/scopes were registered against a ParseTree node in the ParsedModule. Hence, it lives on - with all the sub scopes and symbols. This information in the ParsedModule will be enriched further in additional and different passes.
Now also uses a transient CodeFlowAnalyzer to assess whether variables in instruction blocks has been initialised.
  • Constructor Details

    • SymbolsAndScopes

      public SymbolsAndScopes(ParsedModule parsedModule, ScopeStack scopeStack)
      Create a new instance for symbol and scope management. If these types are not resolved then OK with a big exception and stop processing (so suppress get warning).
  • Method Details

    • getModuleScope

      public ModuleScope getModuleScope()
    • getEk9Types

      public Ek9Types getEk9Types()
    • getCodeFlowAnalyzers

      public List<CodeFlowAnalyzer> getCodeFlowAnalyzers()
      Provide access to the set of code flow analyzers being used for flow analysis.
    • getUninitialisedVariables

      public List<ISymbol> getUninitialisedVariables(IScope inScope)
    • isVariableInitialised

      public boolean isVariableInitialised(ISymbol identifierSymbol, IScope inScope)
    • isVariableInitialised

      public boolean isVariableInitialised(ISymbol identifierSymbol)
    • recordSymbolDeclaration

      public void recordSymbolDeclaration(ISymbol identifierSymbol)
      Records a variable against the appropriate scope.
    • recordSymbolDeclaration

      public void recordSymbolDeclaration(ISymbol identifierSymbol, IScope inScope)
      Record variable declaration against a specific scope.
    • recordSymbolAssignment

      public void recordSymbolAssignment(ISymbol identifierSymbol)
      Record an identifier was assigned and therefore initialised.
    • markSymbolAsInitialised

      public void markSymbolAsInitialised(ISymbol identifierSymbol, IScope inScope)
      Record an identifier was initialised.
    • markSymbolAccessSafe

      public void markSymbolAccessSafe(ISymbol identifierSymbol, IScope inScope)
      Record an identifier as being safe to access.
    • isSymbolAccessSafe

      public boolean isSymbolAccessSafe(ISymbol identifierSymbol, IScope inScope)
      true if identifier is safe to access.
    • enterScope

      public IScope enterScope(org.antlr.v4.runtime.tree.ParseTree ctx)
      Ensure that the correct scope is pushed on to the stack. This method takes a context, retrieves the scope, checks for null and then pushes it.
      Parameters:
      ctx - The contact that must have a scope recorded.
    • enterScope

      public void enterScope(IScope scope)
      To be used to ensure that a scope has been pushed on to the scopeStack.
    • exitScope

      public void exitScope()
      Normally called at the point where the parser listener exits a scope. The scope will be popped off the scopeStack to reveal the parent scope.
    • getTopScope

      public IScope getTopScope()
      Provides access to the top of the scope stack.
    • traverseBackUpStack

      public Optional<IScope> traverseBackUpStack(IScope.ScopeType scopeType)
      Navigates back up the scope stack to find the first match of the scope type passed in.
    • traverseBackUpStackToEnclosingMethod

      public Optional<MethodAndAggregateData> traverseBackUpStackToEnclosingMethod()
      Traversed back up the scope stack to try and locate the enclosing method (if there is one). Clearly the scope might not be within a method and so Optional.empty will result.
      Returns:
      The method and aggregate information if a method was located.
    • traverseBackUpStackToMethodOrFunction

      public Optional<IScopedSymbol> traverseBackUpStackToMethodOrFunction()
    • resolveOrDefine

      public Optional<ISymbol> resolveOrDefine(PossibleGenericSymbol parameterisedSymbol, ErrorListener errorListener)
    • enterNewSymbol

      public void enterNewSymbol(ISymbol symbol, org.antlr.v4.runtime.tree.ParseTree node)
      A new symbol has been encountered and is defined within the current scope in the scope stack and recorded in the parsedModule against the appropriate node.
    • getRecordedSymbol

      public ISymbol getRecordedSymbol(org.antlr.v4.runtime.tree.ParseTree node)
    • getRecordedScope

      public IScope getRecordedScope(org.antlr.v4.runtime.tree.ParseTree node)
    • recordSymbol

      public void recordSymbol(ISymbol symbol, org.antlr.v4.runtime.tree.ParseTree node)
      Record a symbol against a specific node (so it can be looked up later).
    • enterNewLiteral

      public void enterNewLiteral(ISymbol symbol, org.antlr.v4.runtime.tree.ParseTree node)
      For literals, we only record in the parsedModule.
    • enterNewConstant

      public void enterNewConstant(ISymbol symbol, org.antlr.v4.runtime.tree.ParseTree node, SymbolChecker symbolChecker)
      Create a new constant as declared in the constants section and records the symbol.
    • enterModuleScopedSymbol

      public void enterModuleScopedSymbol(IScopedSymbol symbol, org.antlr.v4.runtime.tree.ParseTree node, SymbolChecker symbolChecker)
      To be used when defining a high level symbol at module scope.
    • defineScopedSymbol

      public void defineScopedSymbol(IScopedSymbol symbol, org.antlr.v4.runtime.tree.ParseTree node)
      Record a new scoped symbol in the current scope on the stack. Also records the symbol as both a scope and a symbol in the parsedModule.
    • enterNewScopedSymbol

      public void enterNewScopedSymbol(IScopedSymbol symbol, org.antlr.v4.runtime.tree.ParseTree node)
      Enter a new scoped symbol and record in parsed module as a symbol and as a scope. Then push the scope on to the working scope stack.
    • enterNewScope

      public void enterNewScope(IScope scope, org.antlr.v4.runtime.tree.ParseTree node)
      Enter a new scope and record in both the parsed module and push on to the working scope stack.
    • recordScopeForStackConsistency

      public void recordScopeForStackConsistency(IScope scope, org.antlr.v4.runtime.tree.ParseTree node)
      There are times in parsing/listening when symbols are already defined (due to a developer error). During this situation we cannot define a new node, but we can detect the duplicate (display errors). But so the scope stack does not get corrupted - it is important to still push the existing scope on to the stack. In this way when the stack is popped everything works out.