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                                                        82x     82x 51x       31x 7x       24x 5x       19x 5x       14x 3x                 11x 3x                 8x 5x     3x                           16x     16x 16x                     114x 6x       108x 108x                                 1989x     1989x 1226x       763x 74x       689x 20x     669x          
/**
 * 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;