All files / transpiler/output/codegen/generators/expressions BinaryExprUtils.ts

97.67% Statements 42/43
96.96% Branches 32/33
100% Functions 6/6
97.56% Lines 40/41

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 135                        225x               39x                     6x 6x                       98x   98x 76x     22x 22x 27x 27x   27x   5x 5x   4x 1x   3x 3x   3x 1x   2x 2x   9x 9x   4x 4x   2x       18x                       11x       11x 11x 12x 12x     11x                         42x 1x         41x 1x     40x 1x            
/**
 * Pure utility functions for binary expression generation.
 * Extracted from BinaryExprGenerator for testability (Issue #419).
 */
import LiteralUtils from "../../../../../utils/LiteralUtils";
 
class BinaryExprUtils {
  /**
   * Issue #235: Try to parse a string as a numeric constant.
   * Delegates to LiteralUtils.parseIntegerLiteral to avoid duplication.
   */
  static tryParseNumericLiteral(code: string): number | undefined {
    return LiteralUtils.parseIntegerLiteral(code);
  }
 
  /**
   * ADR-001: Map C-Next equality operator to C.
   * C-Next uses = for equality (mathematical notation), C uses ==.
   */
  static mapEqualityOperator(cnextOp: string): string {
    return cnextOp === "=" ? "==" : cnextOp;
  }
 
  /**
   * ADR-045: Generate strcmp comparison code for string equality.
   */
  static generateStrcmpCode(
    left: string,
    right: string,
    isNotEqual: boolean,
  ): string {
    const cmpOp = isNotEqual ? "!= 0" : "== 0";
    return `strcmp(${left}, ${right}) ${cmpOp}`;
  }
 
  /**
   * Issue #235: Evaluate a constant arithmetic expression.
   * Returns the result if all operands are numeric and evaluation succeeds,
   * undefined otherwise (falls back to non-folded code).
   */
  static tryFoldConstants(
    operandCodes: string[],
    operators: string[],
  ): number | undefined {
    const values = operandCodes.map(BinaryExprUtils.tryParseNumericLiteral);
 
    if (values.includes(undefined)) {
      return undefined;
    }
 
    let result = values[0] as number;
    for (let i = 0; i < operators.length; i++) {
      const op = operators[i];
      const rightValue = values[i + 1] as number;
 
      switch (op) {
        case "*":
          result = result * rightValue;
          break;
        case "/":
          if (rightValue === 0) {
            return undefined;
          }
          result = Math.trunc(result / rightValue);
          break;
        case "%":
          if (rightValue === 0) {
            return undefined;
          }
          result = result % rightValue;
          break;
        case "+":
          result = result + rightValue;
          break;
        case "-":
          result = result - rightValue;
          break;
        default:
          return undefined;
      }
    }
 
    return result;
  }
 
  /**
   * Build a chained binary expression from operands and operators.
   * Used by relational, shift, additive, and multiplicative generators.
   */
  static buildChainedExpression(
    operands: string[],
    operators: string[],
    defaultOp: string,
  ): string {
    Iif (operands.length === 0) {
      return "";
    }
 
    let result = operands[0];
    for (let i = 1; i < operands.length; i++) {
      const op = operators[i - 1] || defaultOp;
      result += ` ${op} ${operands[i]}`;
    }
 
    return result;
  }
 
  /**
   * ADR-017: Validate enum type safety for comparisons.
   * Throws if comparing different enum types or enum to integer.
   */
  static validateEnumComparison(
    leftEnumType: string | null,
    rightEnumType: string | null,
    leftIsInteger: boolean,
    rightIsInteger: boolean,
  ): void {
    if (leftEnumType && rightEnumType && leftEnumType !== rightEnumType) {
      throw new Error(
        `Error: Cannot compare ${leftEnumType} enum to ${rightEnumType} enum`,
      );
    }
 
    if (leftEnumType && rightIsInteger) {
      throw new Error(`Error: Cannot compare ${leftEnumType} enum to integer`);
    }
 
    if (rightEnumType && leftIsInteger) {
      throw new Error(`Error: Cannot compare integer to ${rightEnumType} enum`);
    }
  }
}
 
export default BinaryExprUtils;