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                        231x               40x                     6x 6x                       101x   101x 82x     19x 19x 24x 24x   24x   5x 5x   4x 1x   3x 3x   3x 1x   2x 2x   6x 6x   4x 4x   2x       15x                       11x       11x 11x 12x 12x     11x                         43x 1x         42x 1x     41x 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;