All files / transpiler/logic/symbols/cnext/utils ArrayInitializerUtils.ts

76.36% Statements 42/55
75.86% Branches 44/58
100% Functions 3/3
100% Lines 39/39

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                                        28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x     28x 28x 28x     28x 28x     27x 27x     27x                               16x 3x       13x 13x                       12x 12x   10x 10x   9x          
/**
 * 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 "../../../parser/grammar/CNextParser";
 
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.
   *
   * @param expr The expression context
   * @returns The inferred size, or undefined
   */
  static getInferredSize(expr: Parser.ExpressionContext): number | undefined {
    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;