All files / transpiler/logic/symbols/cnext/collectors StructCollector.ts

100% Statements 38/38
80% Branches 16/20
100% Functions 2/2
100% Lines 38/38

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                                            23x 25x 25x 25x 25x 25x 20x 5x 5x                                         170x 170x 170x   170x   170x 274x 274x 274x   274x   274x 274x 274x     274x 3x 3x   3x 3x     3x 2x             3x 3x   271x   21x 21x             274x           274x 24x     274x     170x                            
/**
 * StructCollector - Extracts struct type declarations from parse trees.
 * Handles fields with types, arrays, and const modifiers.
 */
 
import * as Parser from "../../../parser/grammar/CNextParser";
import ESourceLanguage from "../../../../../utils/types/ESourceLanguage";
import ESymbolKind from "../../../../../utils/types/ESymbolKind";
import IStructSymbol from "../../types/IStructSymbol";
import IFieldInfo from "../../types/IFieldInfo";
import TypeUtils from "../utils/TypeUtils";
import LiteralUtils from "../../../../../utils/LiteralUtils";
 
class StructCollector {
  /**
   * Parse array dimension expressions and append resolved sizes to dimensions array.
   */
  private static parseArrayDimensions(
    arrayDims: Parser.ArrayDimensionContext[],
    dimensions: number[],
    constValues?: Map<string, number>,
  ): void {
    for (const dim of arrayDims) {
      const sizeExpr = dim.expression();
      Eif (sizeExpr) {
        const dimText = sizeExpr.getText();
        const literalSize = LiteralUtils.parseIntegerLiteral(dimText);
        if (literalSize !== undefined) {
          dimensions.push(literalSize);
        E} else if (constValues?.has(dimText)) {
          dimensions.push(constValues.get(dimText)!);
        }
      }
    }
  }
 
  /**
   * Collect a struct declaration and return an IStructSymbol.
   *
   * @param ctx The struct declaration context
   * @param sourceFile Source file path
   * @param scopeName Optional scope name for nested structs
   * @param constValues Map of constant names to their numeric values (for resolving array dimensions)
   * @returns The struct symbol
   */
  static collect(
    ctx: Parser.StructDeclarationContext,
    sourceFile: string,
    scopeName?: string,
    constValues?: Map<string, number>,
  ): IStructSymbol {
    const name = ctx.IDENTIFIER().getText();
    const fullName = scopeName ? `${scopeName}_${name}` : name;
    const line = ctx.start?.line ?? 0;
 
    const fields = new Map<string, IFieldInfo>();
 
    for (const member of ctx.structMember()) {
      const fieldName = member.IDENTIFIER().getText();
      const typeCtx = member.type();
      const fieldType = TypeUtils.getTypeName(typeCtx, scopeName);
      // Note: C-Next struct members don't have const modifier in grammar
      const isConst = false;
 
      const arrayDims = member.arrayDimension();
      const dimensions: number[] = [];
      let isArray = false;
 
      // Handle string types specially
      if (typeCtx.stringType()) {
        const stringCtx = typeCtx.stringType()!;
        const intLiteral = stringCtx.INTEGER_LITERAL();
 
        Eif (intLiteral) {
          const capacity = Number.parseInt(intLiteral.getText(), 10);
 
          // If there are array dimensions, they come BEFORE string capacity
          if (arrayDims.length > 0) {
            StructCollector.parseArrayDimensions(
              arrayDims,
              dimensions,
              constValues,
            );
          }
          // String capacity becomes final dimension (+1 for null terminator)
          dimensions.push(capacity + 1);
          isArray = true;
        }
      } else if (arrayDims.length > 0) {
        // Non-string array
        isArray = true;
        StructCollector.parseArrayDimensions(
          arrayDims,
          dimensions,
          constValues,
        );
      }
 
      const fieldInfo: IFieldInfo = {
        type: fieldType,
        isArray,
        isConst,
      };
 
      if (dimensions.length > 0) {
        fieldInfo.dimensions = dimensions;
      }
 
      fields.set(fieldName, fieldInfo);
    }
 
    return {
      name: fullName,
      parent: scopeName,
      sourceFile,
      sourceLine: line,
      sourceLanguage: ESourceLanguage.CNext,
      isExported: true,
      kind: ESymbolKind.Struct,
      fields,
    };
  }
}
 
export default StructCollector;