All files / utils TypeResolver.ts

100% Statements 37/37
100% Branches 21/21
100% Functions 4/4
100% Lines 36/36

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                        106x 106x 106x 106x                     2649x   2649x 2x       2647x 2647x 44x 44x       2603x 2603x 12x       2591x 3x       2588x 2327x             261x 4x       257x                   12x     12x   12x                       3053x   2782x   41x   223x   2x   1x   1x   1x   1x   1x 2x 1x              
/**
 * TypeResolver - Converts string type representations to TType.
 *
 * This utility handles the conversion from the old string-based type system
 * (e.g., "u32", "string<32>", "u8[10]") to the new TType discriminated union.
 */
import type TType from "../transpiler/types/TType";
import TTypeUtils from "./TTypeUtils";
import PrimitiveKindUtils from "./PrimitiveKindUtils";
import ArrayDimensionText from "./ArrayDimensionText";
 
// Regex patterns for type parsing
const STRING_TYPE_PATTERN = /^string\s*<\s*(\d+)\s*>$/;
const ARRAY_TYPE_PATTERN = /^(.+?)(\[\s*[^\]]+\s*\])+$/;
const EXTERNAL_TYPE_PATTERN = /[:<>]/;
const ENUM_PREFIX_PATTERN = /^E[A-Z]/;
 
class TypeResolver {
  /**
   * Parse a string type representation and return the corresponding TType.
   *
   * @param typeString - String representation of a type (e.g., "u32", "string<32>", "u8[10]")
   * @returns The corresponding TType object
   * @throws Error if the type string is empty or invalid
   */
  static resolve(typeString: string): TType {
    const trimmed = typeString.trim();
 
    if (trimmed === "") {
      throw new Error("Cannot resolve empty type string");
    }
 
    // Check for string type: string<N>
    const stringMatch = STRING_TYPE_PATTERN.exec(trimmed);
    if (stringMatch) {
      const capacity = Number.parseInt(stringMatch[1], 10);
      return TTypeUtils.createString(capacity);
    }
 
    // Check for array type: baseType[dim1][dim2]...
    const arrayMatch = ARRAY_TYPE_PATTERN.exec(trimmed);
    if (arrayMatch) {
      return TypeResolver.parseArrayType(trimmed, arrayMatch[1]);
    }
 
    // Check for external type (contains :: or < > for templates)
    if (EXTERNAL_TYPE_PATTERN.test(trimmed)) {
      return TTypeUtils.createExternal(trimmed);
    }
 
    // Check for primitive type
    if (PrimitiveKindUtils.isPrimitive(trimmed)) {
      return TTypeUtils.createPrimitive(trimmed);
    }
 
    // Check for enum (E prefix convention)
    // LIMITATION: This heuristic assumes enums start with 'E' followed by uppercase.
    // It may misclassify: enums without 'E' prefix, or structs named like 'EFoo'.
    // For accurate resolution, use SymbolRegistry to check against known types.
    if (ENUM_PREFIX_PATTERN.test(trimmed)) {
      return TTypeUtils.createEnum(trimmed);
    }
 
    // Default to struct type
    return TTypeUtils.createStruct(trimmed);
  }
 
  /**
   * Parse an array type and return the TType.
   */
  private static parseArrayType(fullType: string, baseTypeStr: string): TType {
    // Issue #1127: shared with SymbolUtils.parseArrayDimensions. This used a
    // base-10 parseInt, which read "0x10" as 0 and "8+1" as 8 -- a 16-element
    // array reported as dimension 0, and an expression silently truncated.
    const dimensions = ArrayDimensionText.parse(fullType);
 
    // Resolve the base type (recursively handles string<N> as base type)
    const elementType = TypeResolver.resolve(baseTypeStr.trim());
 
    return TTypeUtils.createArray(elementType, dimensions);
  }
 
  /**
   * Convert a TType back to its string representation.
   *
   * This is useful for round-trip compatibility and debugging.
   *
   * @param type - The TType object to convert
   * @returns String representation of the type
   */
  static getTypeName(type: TType): string {
    switch (type.kind) {
      case "primitive":
        return type.primitive;
      case "string":
        return `string<${type.capacity}>`;
      case "struct":
        return type.name;
      case "enum":
        return type.name;
      case "bitmap":
        return type.name;
      case "callback":
        return type.name;
      case "register":
        return type.name;
      case "external":
        return type.name;
      case "array": {
        const elementName = TypeResolver.getTypeName(type.elementType);
        const dims = type.dimensions.map((d) => `[${d}]`).join("");
        return `${elementName}${dims}`;
      }
    }
  }
}
 
export default TypeResolver;