Class AbstractGenerator

java.lang.Object
org.ek9lang.compiler.phase7.generator.AbstractGenerator
Direct Known Subclasses:
AbstractAssertExprGenerator, AbstractShortCircuitGenerator, AbstractStreamStageGenerator, AbstractVariableDeclGenerator, ApplicationClose, AssertStmtGenerator, AssignExpressionToSymbol, AssignIfUnsetBlockGenerator, AssignmentExprInstrGenerator, AssignmentStmtGenerator, BinaryOperationGenerator, BlockStmtInstrGenerator, CallArguments, CallInstrGenerator, CloseChain, ConditionStatementPrelude, ConstructorCallProcessor, ControlFlowChainGenerator, DictLiteralGenerator, DynamicConstructEmitter, ExprInstrGenerator, ForInGenerator, ForRangeGenerator, ForStatementGenerator, FunctionCallProcessor, GuardedConditionEvaluator, GuardedScopeOrchestrator, IfStatementGenerator, InterpolatedStringGenerator, IterationSourceLowering, ListLiteralGenerator, LiteralGenerator, LoopGuardHelper, ObjectAccessInstrGenerator, OperationDfnGenerator, PrimaryReferenceGenerator, PropertyInitialisers, QuestionBlockGenerator, RangeIterationHelper, RequireStmtGenerator, ResourceCloser, ReturningParamProcessor, StatementOwnedCloseables, StmtInstrGenerator, SwitchCaseOrChainGenerator, SwitchStatementGenerator, TextBodyMethodGenerator, ThrowStatementGenerator, TryCatchStatementGenerator, UnaryOperationGenerator, WhileStatementGenerator

abstract class AbstractGenerator extends Object
Base class for all IR generators providing common utilities for safer symbol access, temporary variable creation, and type extraction.

Uses stack-based IRGenerationContext for state management, eliminating parameter threading. All generators inherit access to these safer utility methods to ensure consistency and reduce boilerplate code across the IR generation phase.

Core Principles:

  • Safer Symbol Access - All symbol retrieval includes null and type checks
  • Consistent Debug Info - Debug info is always tied to source locations
  • Reduced Boilerplate - Common patterns are consolidated into helper methods
  • Error Prevention - Invariant violations throw CompilerException with context

Available Safer Utilities:

It holds only the per-generator readers that need no construct state (typeNameOrException, symbolTypeOrException, variableNameForIR). Helpers of the construct are on the GeneratorSet, ONE each (IR tidy W3.9, OPS-11): generators.synthesisedCallMetaData, generators.constantCopyOnAccessLoader; a generator that needs one takes the set.

Usage Examples:

Pattern 1: Safe Symbol Access

// DON'T: Manual symbol retrieval with potential null issues
final var symbol = stackContext.getParsedModule().getRecordedSymbol(ctx);
if (symbol == null) {
  throw new CompilerException("Symbol not found");
}

// DO: Use safer utility with automatic validation
final var symbol = getRecordedSymbolOrException(ctx);

Pattern 2: Temporary Variable Creation

// DON'T: Manual temp variable creation (3 lines)
final var tempName = stackContext.generateTempName();
final var debugInfo = stackContext.createDebugInfo(ctx);
final var tempDetails = new VariableDetails(tempName, debugInfo);

// DO: Use helper method (1 line when only VariableDetails needed)
final var tempDetails = createTempVariableFromContext(ctx);

// If individual components are needed (e.g., for separate use):
final var tempDetails = createTempVariableFromContext(ctx);
final var tempName = tempDetails.resultVariable();
final var debugInfo = tempDetails.debugInfo();

Integration with Other Safer Utilities:

AbstractGenerator works in conjunction with other Phase 7 safer utilities:

  • TypeNameOrException - Extracts fully qualified type name from symbol
  • SymbolTypeOrException - Extracts type symbol with error handling
  • OperationBodyLocator - THE locator of an operation's written body

When to Use Which Utility:

  • Need symbol from ParseTree? Use getRecordedSymbolOrException(ctx)
  • Need type name as String? Use typeNameOrException.apply(symbol)
  • Need type as ISymbol? Use symbolTypeOrException.apply(symbol)
  • Need temp variable? Use createTempVariableFromContext(ctx)
  • Need the return type of a resolved operator? Use a held ResolvedOperatorReturnType (apply(symbol, fallback))

Design Philosophy:

These utilities follow the "fail-fast" principle: if something is wrong (null symbol, missing type, etc.), throw immediately with a descriptive error message rather than propagating null values or invalid state. This makes debugging easier and prevents cascading failures.

See Also:
  • Field Details

    • stackContext

      protected final IRGenerationContext stackContext
    • typeNameOrException

      protected final TypeNameOrException typeNameOrException
    • symbolTypeOrException

      protected final SymbolTypeOrException symbolTypeOrException
      THE shared stateless symbol -> type reading (fail-fast); never construct one per call.
    • variableNameForIR

      protected final VariableNameForIR variableNameForIR
      THE shared stateless symbol -> IR variable name reading; never construct one per call.
  • Constructor Details

    • AbstractGenerator

      AbstractGenerator(IRGenerationContext stackContext)
      Constructor accepting only IRGenerationContext - the single source of state.
  • Method Details

    • getRecordedSymbolOrException

      protected ISymbol getRecordedSymbolOrException(org.antlr.v4.runtime.tree.ParseTree node)
      Safely retrieve the recorded symbol for a parse tree node with automatic validation.

      This is the primary method for accessing symbols during IR generation. It ensures:

      • Symbol was resolved by phases 1-6 (not null)
      • Symbol has an associated type (required for IR generation)

      When to use: ALWAYS use this instead of manually calling stackContext.getParsedModule().getRecordedSymbol(node) to ensure proper validation.

      Parameters:
      node - The parse tree node to retrieve the symbol for
      Returns:
      The validated symbol with guaranteed type information
      Throws:
      IllegalArgumentException - if symbol is null or has no type (compiler bug)
    • createTempVariable

      protected VariableDetails createTempVariable(DebugInfo debugInfo)
      Create a temporary variable with associated debug info.

      This consolidates the common pattern of generating a temp name and wrapping it in VariableDetails. Use this when you already have debug info available (e.g., passed as a method parameter).

      When to use: When debug info is already available. If you have a ParseTree context, use createTempVariableFromContext(ParseTree) instead.

      Example:

      // When debug info is passed in as parameter
      protected List<IRInstr> generateSomething(DebugInfo debugInfo) {
        final var tempDetails = createTempVariable(debugInfo);
        // ... use tempDetails
      }
      
      Parameters:
      debugInfo - The debug information for this variable
      Returns:
      VariableDetails containing the temp variable name and debug info
    • resultVariableOrTemp

      protected String resultVariableOrTemp(VariableDetails resultDetails)
      THE result variable of a construction: the one the caller named, or - in statement context, where there is none - a fresh temp (constructor calls handle the object's lifecycle, so no memory management is needed).
    • createTempVariableFromContext

      protected VariableDetails createTempVariableFromContext(org.antlr.v4.runtime.tree.ParseTree ctx)
      Create a temporary variable with debug info extracted from parse tree context.

      This consolidates the pattern of: createDebugInfo(ctx) → generateTempName() → new VariableDetails(). Preferred method when you have a ParseTree context available.

      When to use: PREFERRED method when you have a ParseTree context (expression, statement, etc.). Automatically extracts source position for accurate debugging.

      Example:

      // Processing list elements
      for (final var exprCtx : listContext.expression()) {
        final var elementDetails = createTempVariableFromContext(exprCtx);
        // If you need the components separately:
        final var elementTemp = elementDetails.resultVariable();
        final var elementDebugInfo = elementDetails.debugInfo();
      }
      
      Parameters:
      ctx - The parse tree context to extract position from
      Returns:
      VariableDetails containing the temp variable name and context-specific debug info
    • processBlockStatements

      protected List<IRInstr> processBlockStatements(EK9Parser.InstructionBlockContext ctx, Function<EK9Parser.BlockStatementContext, List<IRInstr>> blockStmtGenerator)
      Process all block statements in an instruction block. Consolidates the common pattern of iterating over block statements and delegating to a generator.
      Parameters:
      ctx - The instruction block context
      blockStmtGenerator - The generator function for individual block statements
      Returns:
      List of IR instructions from all block statements
    • processBlockStatements

      protected List<IRInstr> processBlockStatements(EK9Parser.BlockContext ctx, Function<EK9Parser.BlockStatementContext, List<IRInstr>> blockStmtGenerator)
      Process all block statements from a BlockContext by unwrapping to InstructionBlockContext. Handles the common pattern of accessing block statements through a BlockContext wrapper.
      Parameters:
      ctx - The block context containing an instruction block
      blockStmtGenerator - The generator function for individual block statements
      Returns:
      List of IR instructions from all block statements
    • addCoverageProbe

      protected void addCoverageProbe(org.antlr.v4.runtime.tree.ParseTree ctx, List<IRInstr> instructions, Function<DebugInfo, IRInstr> probeFactory)
      Add a coverage probe instruction if coverage instrumentation is enabled. Consolidates the common 4-line pattern of creating debug info, producing a probe from a factory, and conditionally adding it to the instruction list.
      Parameters:
      ctx - The parse tree context for source location
      instructions - The instruction list to append the probe to
      probeFactory - Factory function that creates a probe from debug info (may return null)