All files / transpiler/output/codegen/helpers ArrayAccessHelper.ts

100% Statements 25/25
80.76% Branches 21/26
100% Functions 7/7
100% Lines 25/25

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                                        2x 1x   1x               5x 5x                 11x 3x                               3x 1x   2x             9x                     8x 3x             5x 5x   5x 8x     8x 8x     8x   8x 8x   8x                               6x 6x                  
/**
 * Helper utilities for generating array access code.
 * Extracted from CodeGenerator to improve testability.
 */
import IArrayAccessInfo from "../types/IArrayAccessInfo";
import IArrayAccessDeps from "../types/IArrayAccessDeps";
import BitRangeHelper from "./BitRangeHelper";
import CodeGenErrors from "./CodeGenErrors";
 
/**
 * Helper class for array access code generation.
 * Works with an intermediate representation (IArrayAccessInfo)
 * instead of ANTLR parser contexts for testability.
 */
class ArrayAccessHelper {
  /**
   * Generate the complete array access expression.
   * Routes to single-index or bit-range based on accessType.
   */
  static generate(info: IArrayAccessInfo, deps: IArrayAccessDeps): string {
    if (info.accessType === "single-index") {
      return ArrayAccessHelper.generateSingleIndex(info);
    }
    return ArrayAccessHelper.generateBitRange(info, deps);
  }
 
  /**
   * Generate single-index access: array[i]
   * Validates that bitmap types don't use bracket notation.
   */
  static generateSingleIndex(info: IArrayAccessInfo): string {
    ArrayAccessHelper.validateNotBitmap(info);
    return `${info.resolvedName}[${info.indexExpr}]`;
  }
 
  /**
   * Validate bitmap access is not using bracket notation.
   * Bitmaps must use named field access (e.g., flags.FIELD_NAME).
   * @throws Error if attempting bracket indexing on a bitmap type
   */
  static validateNotBitmap(info: IArrayAccessInfo): void {
    if (info.typeInfo?.isBitmap && info.typeInfo.bitmapTypeName) {
      throw CodeGenErrors.bitmapBracketIndexing(
        info.line,
        info.typeInfo.bitmapTypeName,
        info.rawName,
      );
    }
  }
 
  /**
   * Generate bit range access: value[start, width]
   * Handles both integer and float types.
   */
  static generateBitRange(
    info: IArrayAccessInfo,
    deps: IArrayAccessDeps,
  ): string {
    if (ArrayAccessHelper.isFloatBitRange(info.typeInfo)) {
      return ArrayAccessHelper.generateFloatBitRange(info, deps);
    }
    return ArrayAccessHelper.generateIntegerBitRange(info, deps);
  }
 
  /**
   * Determine if this is a float bit range (needs shadow variable).
   */
  static isFloatBitRange(typeInfo: { baseType?: string } | undefined): boolean {
    return typeInfo?.baseType === "f32" || typeInfo?.baseType === "f64";
  }
 
  /**
   * Generate float bit range read with shadow variable.
   * Uses memcpy pattern to safely reinterpret float bits.
   */
  static generateFloatBitRange(
    info: IArrayAccessInfo,
    deps: IArrayAccessDeps,
  ): string {
    if (!deps.isInFunctionBody()) {
      throw CodeGenErrors.floatBitIndexingAtGlobalScope(
        info.rawName,
        info.startExpr ?? "0",
        info.widthExpr ?? "0",
      );
    }
 
    deps.requireInclude("string");
    deps.requireInclude("float_static_assert");
 
    const isF64 = info.typeInfo?.baseType === "f64";
    const shadowType = BitRangeHelper.getShadowType(
      info.typeInfo?.baseType ?? "f32",
    );
    const shadowName = BitRangeHelper.getShadowVarName(info.rawName);
    const mask = deps.generateBitMask(info.widthExpr ?? "0", isF64);
 
    // Register shadow variable if not already declared
    deps.registerFloatShadow(shadowName, shadowType);
 
    const shadowIsCurrent = deps.isShadowCurrent(shadowName);
    deps.markShadowCurrent(shadowName);
 
    return BitRangeHelper.buildFloatBitReadExpr({
      shadowName,
      varName: info.resolvedName,
      start: info.startExpr ?? "0",
      mask,
      shadowIsCurrent,
    });
  }
 
  /**
   * Generate integer bit range read: ((value >> start) & mask)
   */
  static generateIntegerBitRange(
    info: IArrayAccessInfo,
    deps: IArrayAccessDeps,
  ): string {
    const mask = deps.generateBitMask(info.widthExpr ?? "0");
    return BitRangeHelper.buildIntegerBitReadExpr({
      varName: info.resolvedName,
      start: info.startExpr ?? "0",
      mask,
    });
  }
}
 
export default ArrayAccessHelper;