All files / utils LiteralUtils.ts

100% Statements 31/31
97.36% Branches 37/38
100% Functions 4/4
100% Lines 31/31

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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150                                                        77x     77x 47x       30x 7x       23x 5x       18x 5x       13x 3x                 10x 3x                 7x 5x     2x                           16x     16x 16x                     67x 6x       61x 61x                                 1389x     1389x 784x       605x 72x       533x 17x     516x          
/**
 * Utility class for analyzing literal values in the parse tree.
 *
 * Extracted from DivisionByZeroAnalyzer and FloatModuloAnalyzer
 * to eliminate duplicate literal checking code.
 */
 
import * as Parser from "../transpiler/logic/parser/grammar/CNextParser";
 
/**
 * Static utility methods for literal analysis
 */
class LiteralUtils {
  /**
   * Check if a literal represents zero.
   *
   * Handles all C-Next literal formats:
   * - Integer: 0
   * - Hex: 0x0, 0X0
   * - Binary: 0b0, 0B0
   * - Suffixed decimal: 0u8, 0i32, etc.
   * - Suffixed hex: 0x0u8, 0x0i32, etc.
   * - Suffixed binary: 0b0u8, 0b0i32, etc.
   *
   * @param ctx - The literal context from the parse tree
   * @returns true if the literal is zero
   */
  static isZero(ctx: Parser.LiteralContext): boolean {
    const text = ctx.getText();
 
    // Integer literal: exactly "0"
    if (ctx.INTEGER_LITERAL()) {
      return text === "0";
    }
 
    // Hex literal: 0x0 or 0X0
    if (ctx.HEX_LITERAL()) {
      return text === "0x0" || text === "0X0";
    }
 
    // Binary literal: 0b0 or 0B0
    if (ctx.BINARY_LITERAL()) {
      return text === "0b0" || text === "0B0";
    }
 
    // Suffixed decimal: 0u8, 0i32, etc.
    if (ctx.SUFFIXED_DECIMAL()) {
      return text.startsWith("0u") || text.startsWith("0i");
    }
 
    // Suffixed hex: 0x0u8, 0x0i32, etc.
    if (ctx.SUFFIXED_HEX()) {
      return (
        text.startsWith("0x0u") ||
        text.startsWith("0x0i") ||
        text.startsWith("0X0u") ||
        text.startsWith("0X0i")
      );
    }
 
    // Suffixed binary: 0b0u8, 0b0i32, etc.
    if (ctx.SUFFIXED_BINARY()) {
      return (
        text.startsWith("0b0u") ||
        text.startsWith("0b0i") ||
        text.startsWith("0B0u") ||
        text.startsWith("0B0i")
      );
    }
 
    // Issue #1010: Float literals (0.0, 0.0f, .0, etc.)
    if (ctx.FLOAT_LITERAL()) {
      return LiteralUtils.isFloatZero(text);
    }
 
    return false;
  }
 
  /**
   * Check if a float literal string represents zero.
   * Issue #1010: Detect float zero for division-by-zero checking.
   *
   * Handles: 0.0, .0, 0., 0.0f, 0.0F, 0.0e0, 0.0E0, etc.
   *
   * @param text - The float literal text
   * @returns true if the float is zero
   */
  static isFloatZero(text: string): boolean {
    // Remove optional float suffix (f, F)
    const withoutSuffix = text.replace(/[fF]$/, "");
 
    // Parse to number and check if zero
    const value = Number.parseFloat(withoutSuffix);
    return value === 0;
  }
 
  /**
   * Check if a literal is a floating-point number.
   *
   * @param ctx - The literal context from the parse tree
   * @returns true if the literal is a float
   */
  static isFloat(ctx: Parser.LiteralContext): boolean {
    // Check for FLOAT_LITERAL token
    if (ctx.FLOAT_LITERAL()) {
      return true;
    }
 
    // Fallback: check text for decimal point (not in strings)
    const text = ctx.getText();
    return text.includes(".") && !text.startsWith('"');
  }
 
  /**
   * Parse an integer literal string to a numeric value.
   *
   * Handles all C-Next integer formats:
   * - Decimal: 42, -17
   * - Hex: 0x2A, 0X2a
   * - Binary: 0b101010, 0B101010
   *
   * Issue #455: Used for resolving const values in array dimensions.
   *
   * @param text - The literal text to parse
   * @returns The numeric value, or undefined if not a valid integer literal
   */
  static parseIntegerLiteral(text: string): number | undefined {
    const trimmed = text.trim();
 
    // Decimal integer (including negative)
    if (/^-?\d+$/.test(trimmed)) {
      return Number.parseInt(trimmed, 10);
    }
 
    // Hex literal (0x or 0X prefix)
    if (/^0[xX][0-9a-fA-F]+$/.test(trimmed)) {
      return Number.parseInt(trimmed, 16);
    }
 
    // Binary literal (0b or 0B prefix)
    if (/^0[bB][01]+$/.test(trimmed)) {
      return Number.parseInt(trimmed.substring(2), 2);
    }
 
    return undefined;
  }
}
 
export default LiteralUtils;