Class AbstractGenerator

java.lang.Object
org.ek9lang.compiler.phase7.generator.AbstractGenerator
Direct Known Subclasses:
AbstractShortCircuitGenerator, AbstractVariableDeclGenerator, AssertStmtGenerator, AssignExpressionToSymbol, AssignmentExprInstrGenerator, AssignmentStmtGenerator, BinaryOperationGenerator, BlockStmtInstrGenerator, CallInstrGenerator, ConstructorCallProcessor, ControlFlowChainGenerator, ExprInstrGenerator, ForInGenerator, ForRangeGenerator, ForStatementGenerator, FunctionCallProcessor, GuardedAssignmentBlockGenerator, IfStatementGenerator, ListLiteralGenerator, LiteralGenerator, ObjectAccessInstrGenerator, OperationDfnGenerator, PrimaryReferenceGenerator, QuestionBlockGenerator, StmtInstrGenerator, SwitchCaseOrChainGenerator, SwitchStatementGenerator, 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:

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();

Pattern 3: Return Type Extraction

// DON'T: Manual type extraction with multiple conditionals
final var exprSymbol = getRecordedSymbolOrException(ctx);
ISymbol returnType;
if (exprSymbol instanceof CallSymbol cs) {
  final var method = cs.getResolvedSymbolToCall();
  if (method instanceof MethodSymbol ms) {
    returnType = ms.getReturningSymbol().getType().orElse(fallback);
  } else if (method instanceof FunctionSymbol fs) {
    returnType = fs.getReturningSymbol().getType().orElse(fallback);
  } else {
    returnType = fallback;
  }
} else {
  returnType = exprSymbol.getType().orElseThrow();
}

// DO: Use helper method
final var returnType = extractReturnType(ctx, fallbackSymbol);

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
  • OperationDetailContextOrError - Locates operation context or throws

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 return type from call? Use extractReturnType(ctx, 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

  • 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
    • 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
    • extractReturnType

      protected ISymbol extractReturnType(org.antlr.v4.runtime.tree.ParseTree ctx, ISymbol fallbackSymbol)
      Extract return type from a CallSymbol's resolved method. Handles the pattern: CallSymbol → MethodSymbol/FunctionSymbol → returning type. Falls back to expression type if not a CallSymbol.
      Parameters:
      ctx - The parse tree context containing the expression symbol
      fallbackSymbol - Symbol to use if resolved method has no return type
      Returns:
      The return type of the operation
    • 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
    • validateNoPreFlowStatement

      protected void validateNoPreFlowStatement(EK9Parser.PreFlowStatementContext preFlowCtx, String constructName)
      Validate that guards (preFlowStatement) are not present. Throws CompilerException if guards are used, as they're not yet implemented.
      Parameters:
      preFlowCtx - The preFlowStatement context to check (can be null)
      constructName - The name of the control flow construct for error message
      Throws:
      CompilerException - if guards are present
    • validateStatementFormOnly

      protected void validateStatementFormOnly(EK9Parser.ReturningParamContext returningParamCtx, String constructName)
      Validate that expression form (returningParam) is not used. Throws CompilerException if returningParam is present, as expression forms are not yet implemented.
      Parameters:
      returningParamCtx - The returningParam context to check (can be null)
      constructName - The name of the control flow construct for error message
      Throws:
      CompilerException - if expression form is used