Package org.ek9lang.compiler.support
Ek9Compiler
listeners.
The SymbolFactory
and AggregateFactory
are the primary sources of new Symbols or additions to exiting symbols.
There are also other classes like ResolveOrDefineExplicitParameterizedType
that
take generic types and parameters and create new parameterised types.
So this package is mainly focussed on creation of symbols and validation (to some extent).
There are some quite complex processes going on in here, specifically
TypeSubstitution
.
Another set of classes also deal with the thorny issues around defining and creating parameterised types (but take into account chaining the need to create further dependent parameterised types).
ResolverOrDefiner
ResolveOrDefineTypes
ResolveOrDefineExplicitParameterizedType
ResolveOrDefineIdentifierReference
ResolveOrDefineTypeDef
One of the trickiest bits of code is around the creation of parameterised types, this is explained below.
The method
ModuleScope.resolveOrDefine(org.ek9lang.compiler.symbols.PossibleGenericSymbol, org.ek9lang.compiler.common.ErrorListener)
calls into
the CompilableProgram.resolveOrDefine(org.ek9lang.compiler.symbols.PossibleGenericSymbol)
This code checks if a parameterised symbol already exists for what is being asked for (the symbol being asked for
is not fully defined yet as it is just a form of lookup).
This 'lookup' originated in
SymbolFactory.newParameterisedSymbol(org.ek9lang.compiler.symbols.PossibleGenericSymbol, java.util.List)
.
That method delegated to ParameterizedSymbolCreator
.
It was then 'routed via SymbolsAndScopes
, this is a critical component
used in almost all the 'listeners' that are called when the 'ANTLR' AST is traversed.
Anyway, back to the point; There is a little nuance that needs to be highlighted because it is different from how all other symbols are created. Almost all other symbols are created by the traversal of the AST and then the code that the EK9 developer writes gets pushed into the appropriate symbol (i.e. methods, properties, etc.). But when parameterizing generic types (polymorphic types), we have essentially a 'template' but using conceptual types like 'T' for example.
So when we want to 'define or lookup' a parameterized polymorphic type if that type exists in the
appropriate module we just want to reference it. So if the type does exist then we're all good and
ResolvedOrDefineResult
is returned with the symbol and a flag stating that it is
not newly defined.
But if it does not exist, then CompilableProgram
will add the 'skeleton'
PossibleGenericSymbol
to the appropriate module, but will now set the
returning flag to new defined.
This is really critical, because now that 'skeleton' needs to actually be parameterised.
This is done in TypeSubstitution
(it is called by ModuleScope
and it checks if the type is newly defined
or not, if not then it goes about the task of parameterizing it.
The Type Substitution is now driven from the phase of compilation, so the Parameterized Types stay as skeletons for much longer. Only at full resolution are they expanded.
But there's a wrinkle, what if during that parameterization process more parameterized types are needed?
The class TypeSubstitution
does recursive calls back into the
CompilableProgram
calling
CompilableProgram.resolveOrDefine(org.ek9lang.compiler.symbols.PossibleGenericSymbol)
for its needs.
So this is why the construction of parameterized types is in phases, because during parameterization there could
(probably will) be references to other parameterized types that are in the process of being created.
So by recording the 'skeleton' in the appropriate module, the CompilableProgram
will
return 'true' for newly defined the first time, but 'false' on additional requests (even though it is still just
a skeleton). Eventually (just like any recursive function) the stack of Parameterized types being created will
'unwind', resulting in any newly defined Parameterized types being created/record and then having methods and
properties populated with the appropriate types.
The end result is that the first time a Parameterized type is needed it is recorded and populated, but subsequent
requests result in that initial type being returned. This is why the recording aspect is done within the
CompilableProgram
, because access is controlled within a mutex lock.
-
ClassDescriptionUses the symbol and scope management to traverse back up stack to determine if the outer parent is a generic type or not and also if this symbol passed in is a generic type.Support for taking one aggregate and manipulating the methods and the like to be applied to another aggregate.Deals with normal operators (i.e.Checks that the use of the variable only if it has a web variable correlation is valid.If the aggregate is a generic parameterised type this function triggers a check for duplicated methods.Error when the definition of a service is invalid.Error when the definition of a service operator is invalid.Ensures that a try statement/expression is used correctly in or out of an expression.For use when looking to find common types.Attempts to find a common type from the CommonTypeDeterminationDetails or issues errors.Given a list of symbols (normally variables), this code will get the type from each of those symbols.You may think why this mapping.Used to see if it is possible to resolve types, both simple and parametric.Used to model the scenario where a generic type has another dependent generic type used within it.Given some sort of generic type a function or a type with a set of 'type parameters', create a unique internal name for that combination.Error when the definition of an enumerated values could be invalid.Extracts a string for the form "on line 6 in 'filename.ek9'".Extracts a string for the form "on line 6 in 'filename.ek9'".Used when trying to locate the current class or dynamic class, to be able to check if access to fields or methods should be allowed, or even resolve methods without 'this' prefix.Given some sort of generic type a function or a type with a set of 'type parameters', create a unique internal name for that combination.Does a simple check (excluding any inheritance) for duplicated operations (methods, operators) on any sort of Aggregate, i.e.Designed to check if variable name and method names collide with Type/Function names.Designed to gather aspect of operator population.Create a new parameterised symbol from a generic type and a set of type arguments.To be used when trying to define/resolve a parameterised type.Given a parent and a dependent generic type, check that all parameters have been provided with suitable arguments, so that the Parameterized type can be used.This will just create a Aggregate/Function 'parameterized type' of the 'generic type' with the 'type arguments' But note it will have not - repeat NOT have - traversed the 'generic type' and cloned over the methods and altered all the 'T', 'K', 'V' - 'type parameters' for each of the 'type arguments' There is a separate function (TypeSubstitution) that does that.Check if this type being supplied to the test can be used as a program argument.Rather than just check if the actual ISymbols are the same, this code checks if two symbols actually refer to the same source token from the same source file.The bulk of the processing is in the abstract base.The bulk of the processing is in the abstract base.The bulk of the processing is in the abstract base.This is a sort of hybrid resolver or definer function.Used as an abstract base for parameterised types.get the appropriate return type from functions, methods and function delegates and constructors.General utility class that searches in a scope for a symbol to check for duplicates.Just a factory for all types of EK9 symbol.Given some search criteria and a List of Symbols, this class will find the best match.Can be used for a single Simple named search.Given a list of arguments (ISymbols), go through and extract the types out.Extract the valid language value from the String literal or null and error.Converts a list of symbol using their friendly names in to a comma separated list.Given a Symbol that is some form of type, check that it could receive some piped data.Holds the coercions we can make from and to on types.Checks that the symbols in list one and list two are each the same for the corresponding position in the list.oAccepts an aggregate or a function that has been parameterized with 'type arguments'.