All files / PARSE/3-Declare/cnext/utils ArrayInitializerUtils.ts

77.58% Statements 45/58
77.77% Branches 49/63
100% Functions 3/3
100% Lines 42/42

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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134                                          37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x     37x 37x 37x     37x 37x     36x 36x     36x                               25x 4x       21x 21x                                 29x 29x 8x     21x 21x   19x 19x   17x          
/**
 * ArrayInitializerUtils - Shared utilities for analyzing array initializers.
 * Issue #636: Used by both VariableCollector (headers) and CodeGenerator (.c files)
 * to ensure consistent array size inference from initializers.
 */
 
import * as Parser from "../../../2-Parse/grammar/CNextParser";
import StringUtils from "../../../../utils/StringUtils";
 
class ArrayInitializerUtils {
  /**
   * Find an ArrayInitializerContext from an expression.
   * Traverses the parse tree: Expression → Ternary → Or → And → ... → Primary → ArrayInitializer
   *
   * @param expr The expression context to search
   * @returns The array initializer context, or null if not found
   */
  static findArrayInitializer(
    expr: Parser.ExpressionContext,
  ): Parser.ArrayInitializerContext | null {
    // Expression → TernaryExpression
    const ternary = expr.ternaryExpression();
    Iif (!ternary) return null;
 
    // TernaryExpression → OrExpression (when not a ternary)
    const orExpr = ternary.orExpression();
    Iif (!orExpr || orExpr.length === 0) return null;
 
    // OrExpression → AndExpression
    const andExpr = orExpr[0].andExpression();
    Iif (!andExpr || andExpr.length === 0) return null;
 
    // AndExpression → EqualityExpression
    const equality = andExpr[0].equalityExpression();
    Iif (!equality || equality.length === 0) return null;
 
    // EqualityExpression → RelationalExpression
    const relational = equality[0].relationalExpression();
    Iif (!relational || relational.length === 0) return null;
 
    // RelationalExpression → BitwiseOrExpression
    const bitwiseOr = relational[0].bitwiseOrExpression();
    Iif (!bitwiseOr || bitwiseOr.length === 0) return null;
 
    // BitwiseOrExpression → BitwiseXorExpression
    const bitwiseXor = bitwiseOr[0].bitwiseXorExpression();
    Iif (!bitwiseXor || bitwiseXor.length === 0) return null;
 
    // BitwiseXorExpression → BitwiseAndExpression
    const bitwiseAnd = bitwiseXor[0].bitwiseAndExpression();
    Iif (!bitwiseAnd || bitwiseAnd.length === 0) return null;
 
    // BitwiseAndExpression → ShiftExpression
    const shift = bitwiseAnd[0].shiftExpression();
    Iif (!shift || shift.length === 0) return null;
 
    // ShiftExpression → AdditiveExpression
    const additive = shift[0].additiveExpression();
    Iif (!additive || additive.length === 0) return null;
 
    // AdditiveExpression → MultiplicativeExpression
    const multiplicative = additive[0].multiplicativeExpression();
    Iif (!multiplicative || multiplicative.length === 0) return null;
 
    // MultiplicativeExpression → UnaryExpression
    const unaryArr = multiplicative[0].unaryExpression();
    Iif (!unaryArr || unaryArr.length === 0) return null;
    const unary = unaryArr[0];
 
    // UnaryExpression → PostfixExpression
    const postfix = unary.postfixExpression();
    if (!postfix) return null;
 
    // PostfixExpression → PrimaryExpression
    const primary = postfix.primaryExpression();
    Iif (!primary) return null;
 
    // PrimaryExpression → ArrayInitializer
    return primary.arrayInitializer();
  }
 
  /**
   * Count the number of elements in an array initializer.
   * Handles both list syntax [1, 2, 3] and fill-all syntax [0*].
   *
   * @param arrayInit The array initializer context
   * @returns Object with count (for list) or isFillAll (for fill-all syntax)
   */
  static countElements(arrayInit: Parser.ArrayInitializerContext): {
    count: number;
    isFillAll: boolean;
  } {
    // Check for fill-all syntax: [expr*]
    // Fill-all has expression() directly on the arrayInitializer, not wrapped in elements
    if (arrayInit.expression()) {
      return { count: 0, isFillAll: true };
    }
 
    // List syntax: [elem1, elem2, ...]
    const elements = arrayInit.arrayInitializerElement();
    return { count: elements.length, isFillAll: false };
  }
 
  /**
   * Get the inferred array size from an expression containing an array initializer.
   * Returns the element count if the expression is a list-style array initializer,
   * or undefined if no array initializer found or if it uses fill-all syntax.
   *
   * ADR-035: a string literal initializes a `u8` array with its bytes and the
   * terminator (`u8[] message <- "Hello"` is `u8[6]`). Uncounted, the `.h`
   * stated a scalar against the `.c`'s `uint8_t message[] = "Hello"` (#1824
   * review); whether the element type allows a literal is E0866's.
   *
   * @param expr The expression context
   * @returns The inferred size, or undefined
   */
  static getInferredSize(expr: Parser.ExpressionContext): number | undefined {
    const text = expr.getText();
    if (text.length >= 2 && text.startsWith('"') && text.endsWith('"')) {
      return StringUtils.literalLength(text) + 1;
    }
 
    const arrayInit = this.findArrayInitializer(expr);
    if (!arrayInit) return undefined;
 
    const { count, isFillAll } = this.countElements(arrayInit);
    if (isFillAll) return undefined; // Fill-all requires explicit size
 
    return count > 0 ? count : undefined;
  }
}
 
export default ArrayInitializerUtils;