All files / utils TypeCheckUtils.ts

92.85% Statements 13/14
83.33% Branches 5/6
100% Functions 9/9
100% Lines 13/13

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                                                128x                   13056x                   8920x                   1935x                 2252x 2252x 2252x                       2054x                             1503x                   7x 7x                   14x                     145x          
/**
 * Type checking utilities for C-Next types.
 * Pure predicate functions for type classification.
 *
 * Extracted from CodeGenerator.ts as part of ADR-065 decomposition.
 */
 
/**
 * ADR-024's four type lists, imported rather than respelled.
 *
 * #1450: all four were declared here AND in `transpiler/types/`, byte-identical
 * — so a new integer width had to be added in two files, and nothing failed if
 * only one was. That is the same defect `INTEGER_TYPES` records having fixed
 * one level down, which is what makes this the second half of it rather than a
 * new one: it stopped respelling the lists it is built from while its own
 * consumers went on respelling it.
 */
import INTEGER_TYPES from "../types/INTEGER_TYPES";
import UNSIGNED_TYPES from "../types/UNSIGNED_TYPES";
import SIGNED_TYPES from "../types/SIGNED_TYPES";
import FLOAT_TYPES from "../types/FLOAT_TYPES";
import TYPE_WIDTH from "../types/TYPE_WIDTH";
 
/** Standard bit widths for MMIO optimization */
const STANDARD_WIDTHS = [8, 16, 32] as const;
 
class TypeCheckUtils {
  /**
   * Check if a type name is a C-Next integer type.
   *
   * @param typeName - The type name to check
   * @returns true if it's u8, u16, u32, u64, i8, i16, i32, or i64
   */
  static isInteger(typeName: string): boolean {
    return (INTEGER_TYPES as readonly string[]).includes(typeName);
  }
 
  /**
   * Check if a type name is an unsigned integer type.
   *
   * @param typeName - The type name to check
   * @returns true if it's u8, u16, u32, or u64
   */
  static isUnsigned(typeName: string): boolean {
    return (UNSIGNED_TYPES as readonly string[]).includes(typeName);
  }
 
  /**
   * Check if a type name is a signed integer type.
   *
   * @param typeName - The type name to check
   * @returns true if it's i8, i16, i32, or i64
   */
  static isSigned(typeName: string): boolean {
    return (SIGNED_TYPES as readonly string[]).includes(typeName);
  }
 
  /**
   * The values an integer type holds, `[min, max]`, or null for a type that
   * is not a C-Next integer. BigInt, because a u64 bound is past 2^53, where
   * a double stops being exact.
   */
  static integerRange(typeName: string): readonly [bigint, bigint] | null {
    Iif (!TypeCheckUtils.isInteger(typeName)) return null;
    const width = BigInt(TYPE_WIDTH[typeName]);
    return TypeCheckUtils.isUnsigned(typeName)
      ? [0n, (1n << width) - 1n]
      : [-(1n << (width - 1n)), (1n << (width - 1n)) - 1n];
  }
 
  /**
   * Check if a type name is a floating point type.
   *
   * @param typeName - The type name to check
   * @returns true if it's f32 or f64
   */
  static isFloat(typeName: string): boolean {
    return (FLOAT_TYPES as readonly string[]).includes(typeName);
  }
 
  /**
   * Whether a type NAME is a sized string spelling, `string<N>`.
   *
   * Not "is this a string": a bare `string` (`main`'s args) is one too, and
   * that is `OperandTyper.isString`'s question, asked of a typed value. This
   * one reads a spelling, and was named `isString` beside it while the two
   * disagreed on a bare `string` (#1668 review).
   *
   * @param typeName - The type name to check
   * @returns true if it matches string<N> pattern
   */
  static isSizedStringName(typeName: string): boolean {
    return /^string<\d+>$/.test(typeName);
  }
 
  /**
   * Extract capacity from a string type.
   *
   * @param typeName - The string type (e.g., "string<32>")
   * @returns The capacity or null if not a string type
   */
  static getStringCapacity(typeName: string): number | null {
    const match = /^string<(\d+)>$/.exec(typeName);
    return match ? Number.parseInt(match[1], 10) : null;
  }
 
  /**
   * Check if a bit width is a standard MMIO-optimizable width.
   *
   * @param width - The bit width
   * @returns true if it's 8, 16, or 32 bits
   */
  static isStandardWidth(width: number): boolean {
    return (STANDARD_WIDTHS as readonly number[]).includes(width);
  }
 
  /**
   * Check if a type uses native C arithmetic (no overflow checking).
   * Floats overflow to infinity, so they don't need clamp/wrap.
   *
   * @param typeName - The type name
   * @returns true if it's a float type
   */
  static usesNativeArithmetic(typeName: string): boolean {
    return typeName.startsWith("f");
  }
}
 
export default TypeCheckUtils;