c-next 0.1.62 → 0.1.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +86 -63
  2. package/package.json +1 -1
  3. package/src/transpiler/Transpiler.ts +3 -2
  4. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
  6. package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
  7. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  8. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  9. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  10. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
  12. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  13. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  14. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
  16. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  20. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
  21. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
  22. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  24. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
  25. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
  26. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  27. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  28. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  29. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
  30. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  31. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  33. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  34. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  35. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
  36. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
  37. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  38. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  40. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  41. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  42. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  43. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  44. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  45. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  46. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  47. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  50. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  51. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  52. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  53. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  54. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  55. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
@@ -102,6 +102,18 @@ export default class GeneratorRegistry {
102
102
  this.expressions.set(kind, fn as TGeneratorFn<ParserRuleContext>);
103
103
  }
104
104
 
105
+ // =========================================================================
106
+ // Deregistration Methods (for testing error paths)
107
+ // =========================================================================
108
+
109
+ /**
110
+ * Unregister a declaration generator.
111
+ * Primarily used for testing error paths when generators are missing.
112
+ */
113
+ unregisterDeclaration(kind: string): void {
114
+ this.declarations.delete(kind);
115
+ }
116
+
105
117
  // =========================================================================
106
118
  // Query Methods
107
119
  // =========================================================================
@@ -9,7 +9,7 @@ const mockGenerator: TGeneratorFn<ParserRuleContext> = () => ({
9
9
  effects: [],
10
10
  });
11
11
 
12
- export default describe("GeneratorRegistry", () => {
12
+ describe("GeneratorRegistry", () => {
13
13
  let registry: GeneratorRegistry;
14
14
 
15
15
  beforeEach(() => {
@@ -48,4 +48,31 @@ export default describe("GeneratorRegistry", () => {
48
48
  expect(registry.hasExpression("ternary")).toBe(true);
49
49
  });
50
50
  });
51
+
52
+ describe("unregisterDeclaration", () => {
53
+ it("removes a registered declaration", () => {
54
+ registry.registerDeclaration("struct", mockGenerator);
55
+ expect(registry.hasDeclaration("struct")).toBe(true);
56
+
57
+ registry.unregisterDeclaration("struct");
58
+ expect(registry.hasDeclaration("struct")).toBe(false);
59
+ });
60
+
61
+ it("is a no-op for non-existent kind", () => {
62
+ expect(registry.hasDeclaration("unknown")).toBe(false);
63
+ registry.unregisterDeclaration("unknown");
64
+ expect(registry.hasDeclaration("unknown")).toBe(false);
65
+ });
66
+ });
67
+
68
+ describe("getDeclaration", () => {
69
+ it("returns undefined for unregistered kind", () => {
70
+ expect(registry.getDeclaration("struct")).toBeUndefined();
71
+ });
72
+
73
+ it("returns generator for registered kind", () => {
74
+ registry.registerDeclaration("struct", mockGenerator);
75
+ expect(registry.getDeclaration("struct")).toBe(mockGenerator);
76
+ });
77
+ });
51
78
  });
@@ -0,0 +1,67 @@
1
+ /**
2
+ * ArrayDimensionUtils - Shared utilities for generating array dimension strings.
3
+ *
4
+ * Used by StructGenerator and ScopeGenerator for consistent array dimension handling.
5
+ */
6
+
7
+ import * as Parser from "../../../../logic/parser/grammar/CNextParser";
8
+ import IOrchestrator from "../IOrchestrator";
9
+
10
+ /**
11
+ * Generate array type dimension string from arrayType syntax (e.g., u8[16]).
12
+ * Evaluates constants when possible, falls back to expression generation.
13
+ *
14
+ * @param arrayTypeCtx - The arrayType context, or null if not present
15
+ * @param orchestrator - The orchestrator for constant evaluation and expression generation
16
+ * @returns Dimension string like "[16]", "[]", or "" if no arrayType
17
+ */
18
+ function generateArrayTypeDimension(
19
+ arrayTypeCtx: Parser.ArrayTypeContext | null,
20
+ orchestrator: IOrchestrator,
21
+ ): string {
22
+ if (arrayTypeCtx === null) {
23
+ return "";
24
+ }
25
+
26
+ const sizeExpr = arrayTypeCtx.expression();
27
+ if (!sizeExpr) {
28
+ return "[]";
29
+ }
30
+
31
+ const constValue = orchestrator.tryEvaluateConstant(sizeExpr);
32
+ if (constValue === undefined) {
33
+ // Fall back to expression generation for macros, enums, etc.
34
+ return `[${orchestrator.generateExpression(sizeExpr)}]`;
35
+ }
36
+
37
+ return `[${constValue}]`;
38
+ }
39
+
40
+ /**
41
+ * Generate string capacity dimension if applicable.
42
+ * Adds +1 for null terminator.
43
+ *
44
+ * @param typeCtx - The type context to check for string type
45
+ * @returns Dimension string like "[33]" for string<32>, or "" if not a string
46
+ */
47
+ function generateStringCapacityDim(typeCtx: Parser.TypeContext): string {
48
+ const stringCtx = typeCtx.stringType();
49
+ if (!stringCtx) {
50
+ return "";
51
+ }
52
+
53
+ const intLiteral = stringCtx.INTEGER_LITERAL();
54
+ if (!intLiteral) {
55
+ return "";
56
+ }
57
+
58
+ const capacity = Number.parseInt(intLiteral.getText(), 10);
59
+ return `[${capacity + 1}]`;
60
+ }
61
+
62
+ class ArrayDimensionUtils {
63
+ static readonly generateArrayTypeDimension = generateArrayTypeDimension;
64
+ static readonly generateStringCapacityDim = generateStringCapacityDim;
65
+ }
66
+
67
+ export default ArrayDimensionUtils;
@@ -22,6 +22,22 @@ import IOrchestrator from "../IOrchestrator";
22
22
  import TGeneratorFn from "../TGeneratorFn";
23
23
  import generateScopedRegister from "./ScopedRegisterGenerator";
24
24
  import BitmapCommentUtils from "./BitmapCommentUtils";
25
+ import ArrayDimensionUtils from "./ArrayDimensionUtils";
26
+
27
+ /**
28
+ * Generate initializer expression for a variable declaration.
29
+ */
30
+ function generateInitializer(
31
+ varDecl: Parser.VariableDeclarationContext,
32
+ isArray: boolean,
33
+ orchestrator: IOrchestrator,
34
+ ): string {
35
+ if (varDecl.expression()) {
36
+ return ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
37
+ }
38
+ // ADR-015: Zero initialization for uninitialized scope variables
39
+ return ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
40
+ }
25
41
 
26
42
  /**
27
43
  * Extract scoped name from a declaration node.
@@ -81,30 +97,25 @@ function generateScopeVariable(
81
97
  // Issue #375: Check for constructor syntax
82
98
  const constructorArgList = varDecl.constructorArgumentList();
83
99
  if (constructorArgList) {
84
- // ADR-016: All scope variables are emitted at file scope
85
- const type = orchestrator.generateType(varDecl.type());
86
- const fullName = `${scopeName}_${varName}`;
87
- const prefix = isPrivate ? "static " : "";
88
-
89
- // Validate and resolve constructor arguments
90
- const argIdentifiers = constructorArgList.IDENTIFIER();
91
- const line = varDecl.start?.line ?? 0;
92
- const resolvedArgs = resolveConstructorArgs(
93
- argIdentifiers,
100
+ return generateConstructorVariable(
101
+ varDecl,
102
+ varName,
94
103
  scopeName,
95
- line,
104
+ isPrivate,
105
+ constructorArgList,
96
106
  orchestrator,
97
107
  );
98
-
99
- return `${prefix}${type} ${fullName}(${resolvedArgs.join(", ")});`;
100
108
  }
101
109
 
102
110
  // Issue #282: Check if this is a const variable - const values should be inlined
103
111
  const isConst = varDecl.constModifier() !== null;
104
112
 
105
113
  // Issue #500: Check if array before skipping - arrays must be emitted
114
+ // Check both C-style arrayDimension and C-Next style arrayType
115
+ // Use optional chaining for mock compatibility in tests
106
116
  const arrayDims = varDecl.arrayDimension();
107
- const isArray = arrayDims.length > 0;
117
+ const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
118
+ const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
108
119
 
109
120
  // Issue #282: Private const variables should be inlined, not emitted at file scope
110
121
  // Issue #500: EXCEPT arrays - arrays must be emitted as static const
@@ -113,6 +124,60 @@ function generateScopeVariable(
113
124
  return null;
114
125
  }
115
126
 
127
+ return generateRegularVariable(
128
+ varDecl,
129
+ varName,
130
+ scopeName,
131
+ isPrivate,
132
+ orchestrator,
133
+ );
134
+ }
135
+
136
+ /**
137
+ * Generate a constructor-style variable declaration.
138
+ */
139
+ function generateConstructorVariable(
140
+ varDecl: Parser.VariableDeclarationContext,
141
+ varName: string,
142
+ scopeName: string,
143
+ isPrivate: boolean,
144
+ constructorArgList: Parser.ConstructorArgumentListContext,
145
+ orchestrator: IOrchestrator,
146
+ ): string {
147
+ // ADR-016: All scope variables are emitted at file scope
148
+ const type = orchestrator.generateType(varDecl.type());
149
+ const fullName = `${scopeName}_${varName}`;
150
+ const prefix = isPrivate ? "static " : "";
151
+
152
+ // Validate and resolve constructor arguments
153
+ const argIdentifiers = constructorArgList.IDENTIFIER();
154
+ const line = varDecl.start?.line ?? 0;
155
+ const resolvedArgs = resolveConstructorArgs(
156
+ argIdentifiers,
157
+ scopeName,
158
+ line,
159
+ orchestrator,
160
+ );
161
+
162
+ return `${prefix}${type} ${fullName}(${resolvedArgs.join(", ")});`;
163
+ }
164
+
165
+ /**
166
+ * Generate a regular (non-constructor) variable declaration.
167
+ */
168
+ function generateRegularVariable(
169
+ varDecl: Parser.VariableDeclarationContext,
170
+ varName: string,
171
+ scopeName: string,
172
+ isPrivate: boolean,
173
+ orchestrator: IOrchestrator,
174
+ ): string {
175
+ // Derive array and const info from varDecl
176
+ const isConst = varDecl.constModifier() !== null;
177
+ const arrayDims = varDecl.arrayDimension();
178
+ const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
179
+ const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
180
+
116
181
  // ADR-016: All scope variables are emitted at file scope (static-like persistence)
117
182
  const type = orchestrator.generateType(varDecl.type());
118
183
  const fullName = `${scopeName}_${varName}`;
@@ -120,26 +185,22 @@ function generateScopeVariable(
120
185
  const constPrefix = isConst ? "const " : "";
121
186
  const prefix = isPrivate ? "static " : "";
122
187
 
123
- // ADR-036: arrayDimension() now returns an array (arrayDims defined above)
188
+ // Build declaration with all dimensions
124
189
  let decl = `${prefix}${constPrefix}${type} ${fullName}`;
125
- if (isArray) {
190
+ decl += ArrayDimensionUtils.generateArrayTypeDimension(
191
+ arrayTypeCtx,
192
+ orchestrator,
193
+ );
194
+
195
+ if (arrayDims.length > 0) {
196
+ // C-style or additional dimensions
126
197
  decl += orchestrator.generateArrayDimensions(arrayDims);
127
198
  }
199
+
128
200
  // ADR-045: Add string capacity dimension for string arrays
129
- if (varDecl.type().stringType()) {
130
- const stringCtx = varDecl.type().stringType()!;
131
- const intLiteral = stringCtx.INTEGER_LITERAL();
132
- if (intLiteral) {
133
- const capacity = Number.parseInt(intLiteral.getText(), 10);
134
- decl += `[${capacity + 1}]`;
135
- }
136
- }
137
- if (varDecl.expression()) {
138
- decl += ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
139
- } else {
140
- // ADR-015: Zero initialization for uninitialized scope variables
141
- decl += ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
142
- }
201
+ decl += ArrayDimensionUtils.generateStringCapacityDim(varDecl.type());
202
+ decl += generateInitializer(varDecl, isArray, orchestrator);
203
+
143
204
  return decl + ";";
144
205
  }
145
206
 
@@ -458,6 +519,35 @@ function generateScopedBitmapInline(
458
519
  return lines.join("\n");
459
520
  }
460
521
 
522
+ /**
523
+ * Generate a single struct field declaration for scoped struct.
524
+ */
525
+ function generateScopedStructField(
526
+ member: Parser.StructMemberContext,
527
+ orchestrator: IOrchestrator,
528
+ ): string {
529
+ const fieldName = member.IDENTIFIER().getText();
530
+ const fieldType = orchestrator.generateType(member.type());
531
+
532
+ // Handle C-Next style arrayType syntax (u16[8] arr)
533
+ const arrayTypeCtx = member.type().arrayType?.() ?? null;
534
+ let dimStr = ArrayDimensionUtils.generateArrayTypeDimension(
535
+ arrayTypeCtx,
536
+ orchestrator,
537
+ );
538
+
539
+ // Handle C-style array dimensions if present
540
+ const arrayDims = member.arrayDimension();
541
+ if (arrayDims.length > 0) {
542
+ dimStr += orchestrator.generateArrayDimensions(arrayDims);
543
+ }
544
+
545
+ // Handle string capacity for string fields
546
+ dimStr += ArrayDimensionUtils.generateStringCapacityDim(member.type());
547
+
548
+ return ` ${fieldType} ${fieldName}${dimStr};`;
549
+ }
550
+
461
551
  /**
462
552
  * Generate struct inside a scope with proper prefixing.
463
553
  * Struct fields maintain their original types.
@@ -473,27 +563,7 @@ function generateScopedStructInline(
473
563
 
474
564
  // Process struct members
475
565
  for (const member of node.structMember()) {
476
- const fieldName = member.IDENTIFIER().getText();
477
- const fieldType = orchestrator.generateType(member.type());
478
-
479
- // Handle array dimensions if present
480
- const arrayDims = member.arrayDimension();
481
- let dimStr = "";
482
- if (arrayDims.length > 0) {
483
- dimStr = orchestrator.generateArrayDimensions(arrayDims);
484
- }
485
-
486
- // Handle string capacity for string fields
487
- if (member.type().stringType()) {
488
- const stringCtx = member.type().stringType()!;
489
- const intLiteral = stringCtx.INTEGER_LITERAL();
490
- if (intLiteral) {
491
- const capacity = Number.parseInt(intLiteral.getText(), 10);
492
- dimStr += `[${capacity + 1}]`;
493
- }
494
- }
495
-
496
- lines.push(` ${fieldType} ${fieldName}${dimStr};`);
566
+ lines.push(generateScopedStructField(member, orchestrator));
497
567
  }
498
568
 
499
569
  lines.push(`} ${fullName};`, "");
@@ -21,6 +21,86 @@ import IGeneratorOutput from "../IGeneratorOutput";
21
21
  import IOrchestrator from "../IOrchestrator";
22
22
  import TGeneratorFn from "../TGeneratorFn";
23
23
  import TGeneratorEffect from "../TGeneratorEffect";
24
+ import ICodeGenSymbols from "../../../../types/ICodeGenSymbols";
25
+ import ArrayDimensionUtils from "./ArrayDimensionUtils";
26
+
27
+ /**
28
+ * Generate a callback field declaration for a struct.
29
+ */
30
+ function generateCallbackField(
31
+ fieldName: string,
32
+ callbackInfo: { typedefName: string },
33
+ isArray: boolean,
34
+ arrayDims: Parser.ArrayDimensionContext[],
35
+ orchestrator: IOrchestrator,
36
+ ): string {
37
+ if (isArray) {
38
+ const dims = orchestrator.generateArrayDimensions(arrayDims);
39
+ return ` ${callbackInfo.typedefName} ${fieldName}${dims};`;
40
+ }
41
+ return ` ${callbackInfo.typedefName} ${fieldName};`;
42
+ }
43
+
44
+ /**
45
+ * Generate a regular (non-callback) field declaration for a struct.
46
+ */
47
+ function generateRegularField(
48
+ fieldName: string,
49
+ structName: string,
50
+ member: Parser.StructMemberContext,
51
+ isArray: boolean,
52
+ arrayDims: Parser.ArrayDimensionContext[],
53
+ input: IGeneratorInput,
54
+ orchestrator: IOrchestrator,
55
+ ): string {
56
+ const type = orchestrator.generateType(member.type());
57
+
58
+ // Check for arrayType syntax: u8[16] data -> member.type().arrayType()
59
+ // Use optional chaining for mock compatibility in tests
60
+ const arrayTypeCtx = member.type().arrayType?.() ?? null;
61
+ const arrayTypeDimStr = ArrayDimensionUtils.generateArrayTypeDimension(
62
+ arrayTypeCtx,
63
+ orchestrator,
64
+ );
65
+ const hasArrayTypeSyntax = arrayTypeCtx !== null;
66
+
67
+ // Check if we have tracked dimensions for this field (includes string capacity for string arrays)
68
+ const fieldDims = getTrackedFieldDimensions(
69
+ input.symbols,
70
+ structName,
71
+ fieldName,
72
+ );
73
+
74
+ if (fieldDims !== undefined) {
75
+ // Use tracked dimensions (includes string capacity for string arrays)
76
+ const dimsStr = fieldDims.map((d) => `[${d}]`).join("");
77
+ return ` ${type} ${fieldName}${dimsStr};`;
78
+ }
79
+
80
+ if (hasArrayTypeSyntax || isArray) {
81
+ // Combine arrayType dimension (if any) with arrayDimension dimensions
82
+ const dims = orchestrator.generateArrayDimensions(arrayDims);
83
+ return ` ${type} ${fieldName}${arrayTypeDimStr}${dims};`;
84
+ }
85
+
86
+ return ` ${type} ${fieldName};`;
87
+ }
88
+
89
+ /**
90
+ * Get tracked field dimensions from symbols if available.
91
+ */
92
+ function getTrackedFieldDimensions(
93
+ symbols: ICodeGenSymbols | null,
94
+ structName: string,
95
+ fieldName: string,
96
+ ): readonly number[] | undefined {
97
+ if (!symbols) {
98
+ return undefined;
99
+ }
100
+ const trackedDimensions = symbols.structFieldDimensions.get(structName);
101
+ const fieldDims = trackedDimensions?.get(fieldName);
102
+ return fieldDims && fieldDims.length > 0 ? fieldDims : undefined;
103
+ }
24
104
 
25
105
  /**
26
106
  * Generate a C typedef struct from a C-Next struct declaration.
@@ -64,31 +144,28 @@ const generateStruct: TGeneratorFn<Parser.StructDeclarationContext> = (
64
144
  typeName,
65
145
  });
66
146
 
67
- if (isArray) {
68
- const dims = orchestrator.generateArrayDimensions(arrayDims);
69
- lines.push(` ${callbackInfo.typedefName} ${fieldName}${dims};`);
70
- } else {
71
- lines.push(` ${callbackInfo.typedefName} ${fieldName};`);
72
- }
147
+ lines.push(
148
+ generateCallbackField(
149
+ fieldName,
150
+ callbackInfo,
151
+ isArray,
152
+ arrayDims,
153
+ orchestrator,
154
+ ),
155
+ );
73
156
  } else {
74
157
  // Regular field handling
75
- const type = orchestrator.generateType(member.type());
76
-
77
- // Check if we have tracked dimensions for this field (includes string capacity for string arrays)
78
- const trackedDimensions = input.symbols?.structFieldDimensions.get(name);
79
- const fieldDims = trackedDimensions?.get(fieldName);
80
-
81
- if (fieldDims && fieldDims.length > 0) {
82
- // Use tracked dimensions (includes string capacity for string arrays)
83
- const dimsStr = fieldDims.map((d) => `[${d}]`).join("");
84
- lines.push(` ${type} ${fieldName}${dimsStr};`);
85
- } else if (isArray) {
86
- // Fall back to AST dimensions for non-string arrays
87
- const dims = orchestrator.generateArrayDimensions(arrayDims);
88
- lines.push(` ${type} ${fieldName}${dims};`);
89
- } else {
90
- lines.push(` ${type} ${fieldName};`);
91
- }
158
+ lines.push(
159
+ generateRegularField(
160
+ fieldName,
161
+ name,
162
+ member,
163
+ isArray,
164
+ arrayDims,
165
+ input,
166
+ orchestrator,
167
+ ),
168
+ );
92
169
  }
93
170
  }
94
171
 
@@ -0,0 +1,125 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import ArrayDimensionUtils from "../ArrayDimensionUtils";
3
+ import IOrchestrator from "../../IOrchestrator";
4
+
5
+ describe("ArrayDimensionUtils", () => {
6
+ const createMockOrchestrator = (
7
+ constValue?: number,
8
+ exprText = "EXPR",
9
+ ): IOrchestrator => {
10
+ return {
11
+ tryEvaluateConstant: vi.fn().mockReturnValue(constValue),
12
+ generateExpression: vi.fn().mockReturnValue(exprText),
13
+ } as unknown as IOrchestrator;
14
+ };
15
+
16
+ describe("generateArrayTypeDimension", () => {
17
+ it("returns empty string for null context", () => {
18
+ const orchestrator = createMockOrchestrator();
19
+ const result = ArrayDimensionUtils.generateArrayTypeDimension(
20
+ null,
21
+ orchestrator,
22
+ );
23
+ expect(result).toBe("");
24
+ });
25
+
26
+ it("returns [] for context with no expression", () => {
27
+ const orchestrator = createMockOrchestrator();
28
+ const ctx = {
29
+ expression: () => null,
30
+ };
31
+ const result = ArrayDimensionUtils.generateArrayTypeDimension(
32
+ ctx as never,
33
+ orchestrator,
34
+ );
35
+ expect(result).toBe("[]");
36
+ });
37
+
38
+ it("returns constant dimension when evaluable", () => {
39
+ const orchestrator = createMockOrchestrator(16);
40
+ const ctx = {
41
+ expression: () => ({ getText: () => "16" }),
42
+ };
43
+ const result = ArrayDimensionUtils.generateArrayTypeDimension(
44
+ ctx as never,
45
+ orchestrator,
46
+ );
47
+ expect(result).toBe("[16]");
48
+ expect(orchestrator.tryEvaluateConstant).toHaveBeenCalled();
49
+ });
50
+
51
+ it("falls back to expression generation for non-constant", () => {
52
+ const orchestrator = createMockOrchestrator(undefined, "BUFFER_SIZE");
53
+ const mockExpr = { getText: () => "BUFFER_SIZE" };
54
+ const ctx = {
55
+ expression: () => mockExpr,
56
+ };
57
+ const result = ArrayDimensionUtils.generateArrayTypeDimension(
58
+ ctx as never,
59
+ orchestrator,
60
+ );
61
+ expect(result).toBe("[BUFFER_SIZE]");
62
+ expect(orchestrator.generateExpression).toHaveBeenCalledWith(mockExpr);
63
+ });
64
+ });
65
+
66
+ describe("generateStringCapacityDim", () => {
67
+ it("returns empty string for non-string type", () => {
68
+ const ctx = {
69
+ stringType: () => null,
70
+ };
71
+ const result = ArrayDimensionUtils.generateStringCapacityDim(
72
+ ctx as never,
73
+ );
74
+ expect(result).toBe("");
75
+ });
76
+
77
+ it("returns empty string for string without capacity", () => {
78
+ const ctx = {
79
+ stringType: () => ({
80
+ INTEGER_LITERAL: () => null,
81
+ }),
82
+ };
83
+ const result = ArrayDimensionUtils.generateStringCapacityDim(
84
+ ctx as never,
85
+ );
86
+ expect(result).toBe("");
87
+ });
88
+
89
+ it("returns capacity + 1 for string with capacity", () => {
90
+ const ctx = {
91
+ stringType: () => ({
92
+ INTEGER_LITERAL: () => ({
93
+ getText: () => "32",
94
+ }),
95
+ }),
96
+ };
97
+ const result = ArrayDimensionUtils.generateStringCapacityDim(
98
+ ctx as never,
99
+ );
100
+ expect(result).toBe("[33]");
101
+ });
102
+
103
+ it("handles various capacity values", () => {
104
+ const testCases = [
105
+ { input: "8", expected: "[9]" },
106
+ { input: "64", expected: "[65]" },
107
+ { input: "255", expected: "[256]" },
108
+ ];
109
+
110
+ for (const { input, expected } of testCases) {
111
+ const ctx = {
112
+ stringType: () => ({
113
+ INTEGER_LITERAL: () => ({
114
+ getText: () => input,
115
+ }),
116
+ }),
117
+ };
118
+ const result = ArrayDimensionUtils.generateStringCapacityDim(
119
+ ctx as never,
120
+ );
121
+ expect(result).toBe(expected);
122
+ }
123
+ });
124
+ });
125
+ });