Package org.ek9lang.compiler.support
Ek9Compiler listeners.
The SymbolFactory and AggregateManipulator
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).
ResolverOrDefinerResolveOrDefineTypesResolveOrDefineExplicitParameterizedTypeResolveOrDefineIdentifierReferenceResolveOrDefineTypeDef
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.Deals with the creation of various aggregate types.Support for taking one aggregate and manipulating the methods and the like to be applied to another aggregate.THE order of the members of an aggregate body (E01086, Steve 2026-10-11): properties first, then constructors and methods, then operators (an explicit
operator,override operatorordefault operator X, in any order among themselves), and a baredefault operatoras the LAST item of all.The first member of a body out of order.THE declared (target) type anassignmentExpressionis given to - use it, do not re-derive the walk.THE rewrite of a double-quoted String's text as a backtick String — use this; never re-derive it.THE test for a generic's bare type parameterTitself, as opposed to a generic that HAS type parameters.Factory for fairly basic symbols.THE Boolean truth guard rule (Steve, 2026-10-07, decision D9.2) - use this, do not re-derive it.THE identity of a callable in the phase-5 call graph —module::Aggregate.member(T1,T2)for a method,module::Aggregate.memberfor any other aggregate member, and the plain fully-qualified name for everything else.THE "is this symbol one of the variables a dynamic function or class captured?" - by IDENTITY, never by name.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.Checks that a service operation has a security posture, either inherited from its parent service or declared on the operation itself.THE check that a service is written in its one form (Steve, 2026-10-11):forbefore every URI - the service's own (Items for :/items open) and each operation's (byId() as GET for :/{id},operator += for :/) - andasbefore an HTTP verb.Ensures that a try statement/expression is used correctly in or out of an expression.THE re-typing of members that were cloned from a template onto their new owner - use this, never a variant.THE rule that a closeable type is never a type argument (E81061, Steve 2026-10-06, option A - strict).THE decision of what a type'soperator closeis responsible for - use it, never re-derive it.What a type's close closes, and the parent close it then calls.Just for details that are common to all factories.For use when looking to find common types.Which common typeCommonTypeOrErroris asked for.THE common-type deduction - for collections (List/Dict literals, ranges, stream 'cat') and for the operators that SELECT one of their operands (?:, ??, <? <=? >? >=?, the ternary).Given a list of symbols (normally variables), this code will get the type from each of those symbols.Fixed set of constants that tend to be used to 'squirrel' away data - these are the keys.The canonical vocabulary of EK9 comparison-operator glyphs, as named subsets, so that PRE_IR checks reference a single source instead of hand-spelling (and silently diverging) their own switch/Set each time.Evaluates aConstraintExpragainst a compile-time-constant subject value, returning a tri-state verdict.PASS = constraint satisfied; FAIL = provably violated; UNKNOWN = cannot determine statically.You may think why this mapping.THE shared conceptual-to-concrete type replacement - use this, do not re-derive a variant.THE recovery of the base type a CONSTRAINED type narrows — call this, do not re-derive it.THE display name of a construct - the name a person reads for it at RUN TIME (F2, Steve 2026-10-08, Contract 21 inEK9_BACKEND_IR_CONTRACTS.md).THE steps a constructor of an aggregate takes before its own body - use this wherever those steps are generated or modelled, so the generated code and the phase-5 call graph cannot disagree about what construction runs.THE single enumeration of the type references a construct makes through its signature — super (extends), traits (implements), field types (composes), method / function parameter and return types (references), and closed-over captured-variable types (captures).One signature-level type reference: the relationshipkindand the (raw) referenced type.THE copy constructor of a type: a PUBLIC constructor taking exactly one argument of EXACTLY that type -Step(Step).Useful function that generates the decorated/internal name for generic types that have been parameterized.THE decision of which operator a GENERATED comparison is answered by - call this, do not re-derive it.THE test of whether a type already has what adefault operatorwould give it - call this, do not re-derive it.Just focuses on creating directives, these are used when testing EK9 source code.THE single source of truth for the handler set of a dispatcher entry point.THE single source of truth for "what types can actually arrive at this dispatcher parameter".THE single source of truth for "which handler WINS for this concrete type".A winning handler and the assignment cost it won at.Extracts doc comment text from HIDDEN channel tokens.THE predicate for "is this EK9 core" - the modules only the compiler bootstrap may define.THE source for EK9's word keywords, and for whether a keyword can still be used as a NAME.Shared utility for parsing EK9 literals that the grammar supports but standard Java parse methods do not handle directly.THE single source of truth for EK9 literal types — the finite, closed set of built-in types a literal can denote, and the canonical mapping from a parsedEK9Parser.LiteralContextto its fully-qualified EK9 type name.THE validator for an EK9 module name — use this, do not hand-roll a regex.Fixed and Known EK9 types that the compiler needs to know exist.THE test for "is this symbol provided from OUTSIDE this workspace's EK9 source?" — a built-in, or any member of anexternconstruct.Extracts the literal an expression resolves to, or null if it is not a (possibly negated) literal.THE rule of Q-COMPONENT-? (Steve, 2026-10-07): a type with NO fields, counting inherited ones, is set whenever it is present.THE single source of truth for the gated-type → required-capability mapping and its coverage rules.Used to see if it is possible to resolve types, both simple and parametric.THE fields a generated operator (?,$,$$,==,<=>,#?,_fieldSetStatus) walks for one aggregate: its OWN properties, then the variables a dynamic class captured (each becomes a field).THE test of whether a parameterised type made inside a generic's body is a dependent (generic-in-generic) reference of that generic: it is parameterised, still generic in nature, and every bare type parameter it names (at any depth:List of List of T) is one of the generic's OWN type parameters.Used to model the scenario where a generic type has another dependent generic type used within it.THE mapping from a member of a MONOMORPHISED parameterised type back to the member of the GENERIC it was substituted from.THE shared decomposition of a (possibly parameterised) type symbol — use this; do not re-derive theinstanceof PossibleGenericSymbol+getGenericType()/getTypeParameterOrArguments()dance inline.THE rule for a sub-type that adds handlers to a dispatcher it inherits (front-end rationalisation D2, Steve 2026-10-02: "we have little choice but to do 'A'").THE nearest operator ABOVE a type: the declaration a type would inherit, found by walking its super CLASS chain (never its traits) and stopping at the first level that declares it.True if the given parse node sits anywhere inside a 'require' or 'assert' statement.Holds the details for creating and internal 'decorated name' for a generic type that has been parameterised.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.THE "fresh value" predicate: true iff an expression evaluates to a NEW object - one nothing else holds a reference to.Predicate to determine if a symbol is a function delegate.Predicate that returns true iff the symbol IS theorg.ek9.lang::MutexKeygeneric function itself, or a parameterised form of it (e.g.Predicate that returns true iff the call's resolved target is a constructor ofMutexLock(or a parameterisedMutexLock of T).Predicate that returns true iff the symbol IS theorg.ek9.lang::MutexLockgeneric type itself, or a parameterised form of it (e.g.Predicate that returns true iff the call's resolved target is a stdlib method which invokes its function/handler argument on a different thread than the caller.JVM internal type names and descriptors that the compiler uses when generating bytecode.THE shared knowledge of EK9's LAYOUT tokens — INDENT / DEDENT / NL / EOF — and the one correction every consumer of a parse-rule's END position needs because of them.Compares two multi-line strings line-by-line for equality.Generates deterministic internal names using SHA-256 hashing.THE location of a symbol for a message: "on line 6 in 'filename.ek9'".Extracts a string for the form "on line 6 in 'filename.ek9'".THE tolerant reader/writer for a symbol's measuredCommonValues.COMPLEXITY, for consumers that treat "not measured" as zero rather than as an error.A single enforced quality/architecture metric limit, self-describing enough that BOTH the phase-5 checker that fires the error AND the MCP/IDE enquiry layer that reports headroom can read it from the one place — seeMetricLimitsfor the registry and the rationale.How a measured value is compared against the limit.A compound limit that fires only when an absolute cap AND a relative ratio are BOTH exceeded, and only once the module is at leastminConstructsin size (small modules are naturally disconnected and are exempt).A per-genus threshold table:byGenusholds explicit limits,fallbackcovers genera not listed, andexemptgenera are not checked at all (soMetricLimit.GenusLimit.limitFor(SymbolGenus)is empty andMetricLimit.GenusLimit.isOver(int, SymbolGenus)is always false for them).A gate on a fractional score rather than a count — E11020 multiplies a construct's complexity ratio by its statement ratio and fires above 0.50, so neither operand is a limit in its own right and the threshold cannot be an int.A single-threshold limit with a comparator.THE single source of truth for EK9's enforced complexity / coupling / cohesion / dependency-injection metric limits — the exact thresholds the phase-5Acceptable*OrErrorcheckers fire on.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.THE answer to "does this code run every time its enclosing body runs?", read from its position in the parse tree.Rejects parameterising any generic (other thanMutexLockitself) withMutexLock(or a parameterisedMutexLock of T) as a type argument.THE no-argument constructor of a type, whatever its access -Counter().THE check that a construct being defined in a user module does not take the name of a built-in construct (E01090).Checks that a name being defined at module scope does not differ ONLY by case from a name already defined in that same module (across every construct kind - class, record, trait, function, component, service, text, program, application and constant).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.THE single source of the variable names E11031 (NON_DESCRIPTIVE_VARIABLE_NAME) bans - and of everything published about them.One group of banned names with what is published about it.THE check that a body which must return a value can do so: a scope whose every path ends in an exception has an unreachable return (E07380).THE single predicate for "does this super type need an EK9-style super-init call emitted by the IR?".THE shared structural analysis for anobjectAccessExpressionchain — use this, do not re-derive the walk.Designed to gather aspect of operator population.THE parent declarations a method overrides - use this, do not re-derive it.THE error listener that owns a symbol: the listener of the source file that DECLARES it.THE "is this a property of the object whose code is running?" - use it, never re-derive it.THE rule for what a 'defines package' block may hold (E06260): which property NAMES a package defines, and the SHAPE each one's value must have.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 an 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.THE value of a plain"..."String literal - the only String form EK9 reads at compile time: a constant, a constraint, a text's language, a service parameter's name.THE rule for a copy (:=:) through a super-typed reference (Steve, Q5b, 2026-10-08).You may think why this mapping.Just holds a simple set of lists with the appropriate type arguments and their corresponding parameterizing parameters.THE single definition of "can this control-flow pre-flow skip the construct body?".Check if this type being supplied to the test can be used as a program argument.THE reading of phase 5's property-cycle marks (phase5/PropertyCycleMarker) - use this, never the squirrelled data directly.THE answer to "is this node inside a property's initialiser, and which properties of that type are set by then?" Use this wherever a check depends on a property initialiser - do not re-walk the tree for it.THE hint for a QUALIFIED name 'M::X' that did not resolve, when the X the user probably meant is only IMPORTED with 'references' - by module M, or by the module the name is used in.THE symbol-identity walk over a parse-tree fragment for the compiler and themcp.oracletools - use this (moved down from compiler-services withSymbolWrites, 2026-10-04), never a by-NAME walk (a regex over body text,getText().equals(name)on identifier nodes, a set of names).One identifier occurrence the compiler bound to a symbol.THE reading of a statement that makes a local variable (never a property) refer to the SAME object an expression evaluates to - a reference copy (an alias), never a value copy.Utility to determine if an ISymbol represents a reference-counted type that should be included in the traceableFields list during initial IR generation.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.THE reading of what a call's RESULT may share with what it was called on - the return half of theCommonValues.RETAINSdeclaration.get the appropriate return type from functions, methods and function delegates and constructors.Checks to see if the type parameters are the same as the generic arguments.THE vocabulary of B18 - "a type defines sameness once" - call this, do not re-derive it.Checks for inappropriate use of 'sanitized' in expression parameters.THE reading of a statement that stores the object in one of its OWN properties -me: this,next := this,this.owner :=? this- a property of the object's own type that refers back to it.A statement that gives one of the object's own properties a dynamic function or class capturing 'this'.Deals with the creation of the service parts for the EK9 language.THE normalisation of a service route's URI-template parameters for route-identity comparison — use this, do not re-derive the\{.*?}regex.THE shared cache of small non-negative int-to-String conversions — use this instead ofString.valueOf(int)/Integer.toString(int)when the result is squirrelled or otherwise retained per symbol.THE source of a parse rule as the developer wrote it: the characters it spans in the char stream, spaces kept.THE mark that a call's closeable result is owned by the block statement that makes it, so that statement closes it when it finishes (the unnamed half of the closeable ownership rule, Steve 2026-10-05).Just deals with the creation of Streams parts.THE classification of stream stages by what they need from the stream - use this, never a list of stage tokens of your own.The outcome of walking a pipeline's stages from a source that is not bounded.THE strongly connected components of a directed graph (Tarjan, 1972) - use this wherever a phase needs the cycles of a graph, never a private copy.One strongly connected component.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.THE "where is this symbol written?" for the compiler's own analyses and themcp.oracletools - use this, never a by-name test such asbody.contains("this." + name)or a set of assigned NAMES.One write.THE names the compiler gives the parameters of constructors it SYNTHESISES - a constrained type's, an enumeration's, a text's, a generic's required pair and its type parameters'.THE rule for target-typed generic construction - use it, never add a second inference route (Steve, 2026-10-08, GENERIC CONSTRUCTION a).Deals with the creation of Text parts from the EK9 language.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.oTHE constraint of a generic's type parameter: the 'X' ofT constrain by X, or empty for an unconstrained 'T' - use this, never "does T have a super?".Accepts an aggregate or a function that has been parameterized with 'type arguments'.Does a few checks on a PossibleGenericSymbol in terms of checking types and the generic type it is configured with.THE rule thatVoidis never the type of a value (E10011, the formal specification's WF-VOID-RESTRICTION).