or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

atn-dfa.mdcontext.mdcore-classes.mderror-handling.mdindex.mdparse-trees.mdstreams.mdutilities.md
tile.json

context.mddocs/

Context

Context management for parse rules and prediction contexts that maintain parsing state and enable advanced parsing features. These classes represent the hierarchical structure of rule invocations during parsing.

Capabilities

Rule Context Classes

Core context classes representing parser rule invocations.

/**
 * Base context class for parse rules
 */
class RuleContext extends RuleNode {
  /** Parent context in the call stack */
  parentCtx: RuleContext | undefined;
  
  /** State number that invoked this rule */
  invokingState: number;
  
  /**
   * Get this context (identity function for compatibility)
   * @returns This rule context
   */
  get ruleContext(): RuleContext;
  
  /**
   * Convert context to string tree representation
   * @param ruleNames - Array of rule names for display (optional)
   * @param recog - Parser for additional context (optional)
   * @returns String representation of context tree
   */
  toStringTree(ruleNames: string[] | null, recog: Parser): string;
}

/**
 * Parser rule context with parse tree information
 */
class ParserRuleContext extends RuleContext {
  /** Start token of this rule */
  start: Token;
  
  /** Stop token of this rule (optional, may be null during parsing) */
  stop: Token | undefined;
  
  /** Child parse tree nodes (null if no children) */
  children: ParseTree[] | null;
  
  /** Parent parser rule context */
  parentCtx: ParserRuleContext | undefined;
  
  /** Recognition exception if rule failed (optional) */
  exception?: RecognitionException;
  
  /** Parser that created this context (optional) */
  parser?: Parser;
  
  /**
   * Create parser rule context
   * @param parent - Parent context (optional)
   * @param invokingStateNumber - Invoking state number (optional)
   */
  constructor(parent?: ParserRuleContext, invokingStateNumber?: number);
  
  /**
   * Copy properties from another context
   * @param ctx - Context to copy from
   */
  copyFrom(ctx: ParserRuleContext): void;
  
  /**
   * Get number of child parse tree nodes
   * @returns Number of children
   */
  getChildCount(): number;
  
  /**
   * Get child parse tree node at index
   * @param i - Child index
   * @returns Parse tree child at index
   */
  getChild(i: number): ParseTree;
  
  /**
   * Get first terminal node of specified token type
   * @param ttype - Token type to find
   * @param i - Index of occurrence (0-based)
   * @returns Terminal node or null if not found
   */
  getToken(ttype: number, i: number): TerminalNode;
  
  /**
   * Get all terminal nodes of specified token type
   * @param ttype - Token type to find
   * @returns Array of terminal nodes
   */
  getTokens(ttype: number): TerminalNode[];
  
  /**
   * Get typed rule context of specific type at index
   * @param ctxType - Constructor for rule context type
   * @param i - Index of occurrence (0-based)
   * @returns Typed rule context or null if not found
   */
  getTypedRuleContext<T extends ParserRuleContext, P extends Parser>(
    ctxType: { new (parser?: P, parent?: ParserRuleContext, invokingState?: number, ...args: any[]): T}, 
    i: number
  ): T;
  
  /**
   * Get all typed rule contexts of specific type
   * @param ctxType - Constructor for rule context type
   * @returns Array of typed rule contexts
   */
  getTypedRuleContexts<T extends ParserRuleContext, P extends Parser>(
    ctxType: { new (parser?: P, parent?: ParserRuleContext, invokingState?: number, ...args: any[]): T}
  ): T[];
}

Usage Examples

Working with parser rule contexts:

import { ParserRuleContext, TerminalNode } from "antlr4";

// Example of a generated rule context class
class ExpressionContext extends ParserRuleContext {
  constructor(parser?: Parser, parent?: ParserRuleContext, invokingState?: number) {
    super(parent, invokingState);
    this.parser = parser;
  }
  
  // Methods generated by ANTLR for accessing specific parts of the rule
  getNumber(): TerminalNode | null {
    return this.getToken(MyParser.NUMBER, 0);
  }
  
  getLeftExpression(): ExpressionContext | null {
    return this.getTypedRuleContext(ExpressionContext, 0);
  }
  
  getRightExpression(): ExpressionContext | null {
    return this.getTypedRuleContext(ExpressionContext, 1);
  }
  
  getOperator(): TerminalNode | null {
    // Get operator token (could be +, -, *, /)
    let op = this.getToken(MyParser.PLUS, 0);
    if (!op) op = this.getToken(MyParser.MINUS, 0);
    if (!op) op = this.getToken(MyParser.MULTIPLY, 0);
    if (!op) op = this.getToken(MyParser.DIVIDE, 0);
    return op;
  }
}

Traversing rule context hierarchy:

import { ParserRuleContext } from "antlr4";

function printContextHierarchy(ctx: ParserRuleContext | undefined, depth: number = 0): void {
  if (!ctx) return;
  
  const indent = "  ".repeat(depth);
  const ruleName = ctx.constructor.name;
  const startToken = ctx.start ? `[${ctx.start.line}:${ctx.start.column}]` : '';
  
  console.log(`${indent}${ruleName} ${startToken}`);
  
  // Print children
  if (ctx.children) {
    for (let i = 0; i < ctx.children.length; i++) {
      const child = ctx.children[i];
      if (child instanceof ParserRuleContext) {
        printContextHierarchy(child, depth + 1);
      } else {
        console.log(`${indent}  Terminal: "${child.getText()}"`);
      }
    }
  }
  
  // Could also traverse up the parent chain:
  // printContextHierarchy(ctx.parentCtx, depth + 1);
}

// Usage
const tree = parser.expression();
if (tree instanceof ParserRuleContext) {
  printContextHierarchy(tree);
}

Extracting specific information from contexts:

import { ParserRuleContext, TerminalNode } from "antlr4";

// Helper function to extract all identifiers from an expression
function extractIdentifiers(ctx: ParserRuleContext): string[] {
  const identifiers: string[] = [];
  
  function visit(node: ParserRuleContext): void {
    // Get all identifier tokens (assuming IDENTIFIER is token type 5)
    const identTokens = node.getTokens(5); // MyParser.IDENTIFIER
    for (const token of identTokens) {
      identifiers.push(token.getText());
    }
    
    // Recursively visit child rule contexts
    if (node.children) {
      for (const child of node.children) {
        if (child instanceof ParserRuleContext) {
          visit(child);
        }
      }
    }
  }
  
  visit(ctx);
  return identifiers;
}

// Usage
const expression = parser.expression();
const identifiers = extractIdentifiers(expression);
console.log("Found identifiers:", identifiers);

Creating custom rule context with additional data:

import { ParserRuleContext, Parser } from "antlr4";

// Custom context with additional semantic information
class EnhancedExpressionContext extends ParserRuleContext {
  private _evaluatedValue?: number;
  private _variableBindings = new Map<string, number>();
  
  constructor(parser?: Parser, parent?: ParserRuleContext, invokingState?: number) {
    super(parent, invokingState);
    this.parser = parser;
  }
  
  // Evaluate expression and cache result
  evaluate(variables: Map<string, number> = new Map()): number {
    if (this._evaluatedValue !== undefined) {
      return this._evaluatedValue;
    }
    
    // Example evaluation logic
    const numberToken = this.getToken(MyParser.NUMBER, 0);
    if (numberToken) {
      this._evaluatedValue = parseFloat(numberToken.getText());
      return this._evaluatedValue;
    }
    
    const identToken = this.getToken(MyParser.IDENTIFIER, 0);
    if (identToken) {
      const value = variables.get(identToken.getText());
      if (value === undefined) {
        throw new Error(`Undefined variable: ${identToken.getText()}`);
      }
      this._evaluatedValue = value;
      return this._evaluatedValue;
    }
    
    // Binary operation
    const left = this.getTypedRuleContext(EnhancedExpressionContext, 0);
    const right = this.getTypedRuleContext(EnhancedExpressionContext, 1);
    const op = this.getOperator();
    
    if (left && right && op) {
      const leftVal = left.evaluate(variables);
      const rightVal = right.evaluate(variables);
      
      switch (op.getText()) {
        case '+': this._evaluatedValue = leftVal + rightVal; break;
        case '-': this._evaluatedValue = leftVal - rightVal; break;
        case '*': this._evaluatedValue = leftVal * rightVal; break;
        case '/': this._evaluatedValue = leftVal / rightVal; break;
        default: throw new Error(`Unknown operator: ${op.getText()}`);
      }
    }
    
    return this._evaluatedValue || 0;
  }
  
  getOperator(): TerminalNode | null {
    // Implementation from previous example
    let op = this.getToken(MyParser.PLUS, 0);
    if (!op) op = this.getToken(MyParser.MINUS, 0);
    if (!op) op = this.getToken(MyParser.MULTIPLY, 0);
    if (!op) op = this.getToken(MyParser.DIVIDE, 0);
    return op;
  }
  
  // Get all variable dependencies
  getVariableDependencies(): Set<string> {
    const deps = new Set<string>();
    
    const identToken = this.getToken(MyParser.IDENTIFIER, 0);
    if (identToken) {
      deps.add(identToken.getText());
    }
    
    // Recursively collect from children
    if (this.children) {
      for (const child of this.children) {
        if (child instanceof EnhancedExpressionContext) {
          for (const dep of child.getVariableDependencies()) {
            deps.add(dep);
          }
        }
      }
    }
    
    return deps;
  }
}

Context-aware error reporting:

import { ParserRuleContext, RecognitionException, ErrorListener } from "antlr4";

class ContextAwareErrorListener extends ErrorListener<Token> {
  syntaxError(recognizer: Recognizer<Token>, offendingSymbol: Token, line: number, column: number, msg: string, e: RecognitionException | undefined): void {
    if (recognizer instanceof Parser) {
      const parser = recognizer as Parser;
      const context = parser._ctx;
      
      console.error(`Syntax error at ${line}:${column}: ${msg}`);
      console.error(`Context: ${this.getContextDescription(context)}`);
      
      if (offendingSymbol) {
        console.error(`Offending token: "${offendingSymbol.text}"`);
      }
      
      // Show context hierarchy
      console.error("Rule stack:");
      this.printRuleStack(context);
    }
  }
  
  private getContextDescription(ctx: ParserRuleContext): string {
    const ruleName = ctx.constructor.name;
    const start = ctx.start ? `line ${ctx.start.line}` : 'unknown';
    return `${ruleName} (started at ${start})`;
  }
  
  private printRuleStack(ctx: ParserRuleContext | undefined, depth: number = 0): void {
    if (!ctx) return;
    
    const indent = "  ".repeat(depth);
    const desc = this.getContextDescription(ctx);
    console.error(`${indent}${desc}`);
    
    if (ctx.parentCtx instanceof ParserRuleContext) {
      this.printRuleStack(ctx.parentCtx, depth + 1);
    }
  }
  
  // Other required methods
  reportAmbiguity(): void {}
  reportAttemptingFullContext(): void {}
  reportContextSensitivity(): void {}
}

// Usage
const parser = new MyParser(tokens);
parser.removeErrorListeners();
parser.addErrorListener(new ContextAwareErrorListener());

Building custom parse tree from contexts:

import { ParserRuleContext, ParseTree, TerminalNode } from "antlr4";

interface CustomNode {
  type: 'rule' | 'terminal';
  name: string;
  text?: string;
  children?: CustomNode[];
  line?: number;
  column?: number;
}

function buildCustomTree(node: ParseTree): CustomNode {
  if (node instanceof TerminalNode) {
    return {
      type: 'terminal',
      name: 'Terminal',
      text: node.getText(),
      line: node.symbol.line,
      column: node.symbol.column
    };
  } else if (node instanceof ParserRuleContext) {
    const children: CustomNode[] = [];
    
    if (node.children) {
      for (const child of node.children) {
        children.push(buildCustomTree(child));
      }
    }
    
    return {
      type: 'rule',
      name: node.constructor.name,
      children: children.length > 0 ? children : undefined,
      line: node.start?.line,
      column: node.start?.column
    };
  }
  
  throw new Error(`Unknown node type: ${node.constructor.name}`);
}

// Usage
const parseTree = parser.expression();
const customTree = buildCustomTree(parseTree);
console.log(JSON.stringify(customTree, null, 2));

Context validation and semantic checks:

import { ParserRuleContext } from "antlr4";

class SemanticValidator {
  private errors: string[] = [];
  
  validateExpression(ctx: ExpressionContext): boolean {
    this.errors = [];
    this.visitExpression(ctx);
    return this.errors.length === 0;
  }
  
  private visitExpression(ctx: ExpressionContext): void {
    // Check for division by zero
    const operator = ctx.getOperator();
    if (operator && operator.getText() === '/') {
      const rightExpr = ctx.getRightExpression();
      if (rightExpr) {
        const numberToken = rightExpr.getNumber();
        if (numberToken && numberToken.getText() === '0') {
          this.errors.push(`Division by zero at line ${numberToken.symbol.line}`);
        }
      }
    }
    
    // Recursively validate child expressions
    const childExpressions = ctx.getTypedRuleContexts(ExpressionContext);
    for (const childExpr of childExpressions) {
      this.visitExpression(childExpr);
    }
    
    // Check for undefined variables (example)
    const identifiers = ctx.getTokens(MyParser.IDENTIFIER);
    for (const ident of identifiers) {
      if (!this.isVariableDefined(ident.getText())) {
        this.errors.push(`Undefined variable '${ident.getText()}' at line ${ident.symbol.line}`);
      }
    }
  }
  
  private isVariableDefined(name: string): boolean {
    // Example: check against known variables
    const knownVars = ['x', 'y', 'z', 'pi', 'e'];
    return knownVars.includes(name);
  }
  
  getErrors(): string[] {
    return this.errors.slice();
  }
}

// Usage
const expression = parser.expression();
const validator = new SemanticValidator();

if (validator.validateExpression(expression)) {
  console.log("Expression is semantically valid");
} else {
  console.error("Semantic errors found:");
  validator.getErrors().forEach(error => console.error(`  ${error}`));
}