All files / transpiler/output/codegen/utils CodegenParserUtils.ts

100% Statements 26/26
100% Branches 21/21
100% Functions 3/3
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99                                                          172x 172x 526x 177x     172x                             701x 696x     5x 5x 1x     4x 4x 4x     4x 1x       3x 3x                           140x 140x     129x   96x 96x   69x          
/**
 * Parser utilities specific to code generation.
 *
 * These utilities depend on ANTLR parser types and are used by CodeGenerator.
 * Separated from src/utils/ParserUtils.ts to avoid circular dependencies.
 */
 
import { ParserRuleContext, TerminalNode } from "antlr4ng";
import * as Parser from "../../../logic/parser/grammar/CNextParser";
import ExpressionUnwrapper from "./ExpressionUnwrapper";
 
/**
 * Static utility methods for parser context operations in code generation.
 */
class CodegenParserUtils {
  /**
   * Extract operators from parse tree children in order.
   *
   * When parsing expressions like "a + b - c", ANTLR creates children
   * with operands interleaved: [a, +, b, -, c]. This method extracts
   * just the operators as terminal nodes.
   *
   * Note: Using children.filter() loses operator ordering when operators
   * are detected using text.includes(), so we iterate explicitly.
   *
   * @param ctx - The parser rule context containing operands and operators
   * @returns Array of operator strings in the order they appear
   */
  static getOperatorsFromChildren(ctx: ParserRuleContext): string[] {
    const operators: string[] = [];
    for (const child of ctx.children) {
      if (child instanceof TerminalNode) {
        operators.push(child.getText());
      }
    }
    return operators;
  }
 
  /**
   * Check if this is the main function with command-line args parameter.
   * Supports: u8 args[][] (legacy) or string args[] (preferred)
   *
   * @param name - Function name
   * @param paramList - Parameter list context
   * @returns true if this is main with args parameter
   */
  static isMainFunctionWithArgs(
    name: string,
    paramList: Parser.ParameterListContext | null,
  ): boolean {
    if (name !== "main" || !paramList) {
      return false;
    }
 
    const params = paramList.parameter();
    if (params.length !== 1) {
      return false;
    }
 
    const param = params[0];
    const typeCtx = param.type();
    const dims = param.arrayDimension();
 
    // Check for string args[] (preferred - array of strings)
    if (typeCtx.stringType() && dims.length === 1) {
      return true;
    }
 
    // Check for u8 args[][] (legacy - 2D array of bytes)
    const type = typeCtx.getText();
    return (type === "u8" || type === "i8") && dims.length === 2;
  }
 
  /**
   * Extract a simple identifier from an expression, if it is one.
   * Returns null for complex expressions (binary ops, function calls, etc.)
   *
   * A "simple identifier" is an expression that is just a variable name
   * with no operators, member access, or array indexing.
   *
   * @param ctx - The expression context to analyze
   * @returns The identifier string, or null if not a simple identifier
   */
  static getSimpleIdentifier(ctx: Parser.ExpressionContext): string | null {
    const postfix = ExpressionUnwrapper.getPostfixExpression(ctx);
    if (!postfix) return null;
 
    // Has postfix operators like . or [] - not a simple identifier
    if (postfix.postfixOp().length !== 0) return null;
 
    const primary = postfix.primaryExpression();
    if (!primary.IDENTIFIER()) return null;
 
    return primary.IDENTIFIER()!.getText();
  }
}
 
export default CodegenParserUtils;