Package org.ek9lang.compiler.common
Class SymbolsAndScopes
java.lang.Object
org.ek9lang.compiler.common.SymbolsAndScopes
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.
Now also uses a transient CodeFlowAnalyzer to assess whether variables in instruction blocks has been initialised.
-
Constructor Summary
ConstructorDescriptionSymbolsAndScopes
(ParsedModule parsedModule, ScopeStack scopeStack) Create a new instance for symbol and scope management. -
Method Summary
Modifier and TypeMethodDescriptionvoid
defineScopedSymbol
(IScopedSymbol symbol, org.antlr.v4.runtime.tree.ParseTree node) Record a new scoped symbol in the current scope on the stack.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.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.void
enterNewLiteral
(ISymbol symbol, org.antlr.v4.runtime.tree.ParseTree node) For literals, we only record in the parsedModule.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.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.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.enterScope
(org.antlr.v4.runtime.tree.ParseTree ctx) Ensure that the correct scope is pushed on to the stack.void
enterScope
(IScope scope) To be used to ensure that a scope has been pushed on to the scopeStack.void
Normally called at the point where the parser listener exits a scope.Provide access to the set of code flow analyzers being used for flow analysis.getRecordedScope
(org.antlr.v4.runtime.tree.ParseTree node) getRecordedSymbol
(org.antlr.v4.runtime.tree.ParseTree node) Provides access to the top of the scope stack.getUninitialisedVariables
(IScope inScope) boolean
isSymbolAccessSafe
(ISymbol identifierSymbol, IScope inScope) true if identifier is safe to access.boolean
isVariableInitialised
(ISymbol identifierSymbol) boolean
isVariableInitialised
(ISymbol identifierSymbol, IScope inScope) void
markSymbolAccessSafe
(ISymbol identifierSymbol, IScope inScope) Record an identifier as being safe to access.void
markSymbolAsInitialised
(ISymbol identifierSymbol, IScope inScope) Record an identifier was initialised.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).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).void
recordSymbolAssignment
(ISymbol identifierSymbol) Record an identifier was assigned and therefore initialised.void
recordSymbolDeclaration
(ISymbol identifierSymbol) Records a variable against the appropriate scope.void
recordSymbolDeclaration
(ISymbol identifierSymbol, IScope inScope) Record variable declaration against a specific scope.resolveOrDefine
(PossibleGenericSymbol parameterisedSymbol, ErrorListener errorListener) traverseBackUpStack
(IScope.ScopeType scopeType) Navigates back up the scope stack to find the first match of the scope type passed in.Traversed back up the scope stack to try and locate the enclosing method (if there is one).
-
Constructor Details
-
SymbolsAndScopes
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
-
getEk9Types
-
getCodeFlowAnalyzers
Provide access to the set of code flow analyzers being used for flow analysis. -
getUninitialisedVariables
-
isVariableInitialised
-
isVariableInitialised
-
recordSymbolDeclaration
Records a variable against the appropriate scope. -
recordSymbolDeclaration
Record variable declaration against a specific scope. -
recordSymbolAssignment
Record an identifier was assigned and therefore initialised. -
markSymbolAsInitialised
Record an identifier was initialised. -
markSymbolAccessSafe
Record an identifier as being safe to access. -
isSymbolAccessSafe
true if identifier is safe to access. -
enterScope
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
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
Provides access to the top of the scope stack. -
traverseBackUpStack
Navigates back up the scope stack to find the first match of the scope type passed in. -
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
-
resolveOrDefine
public Optional<ISymbol> resolveOrDefine(PossibleGenericSymbol parameterisedSymbol, ErrorListener errorListener) -
enterNewSymbol
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
-
getRecordedScope
-
recordSymbol
Record a symbol against a specific node (so it can be looked up later). -
enterNewLiteral
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
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
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
Enter a new scope and record in both the parsed module and push on to the working scope stack. -
recordScopeForStackConsistency
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.
-