c-next 0.1.64 → 0.1.66

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 (33) hide show
  1. package/grammar/CNext.g4 +9 -2
  2. package/package.json +5 -1
  3. package/src/transpiler/logic/parser/grammar/CNext.interp +2 -1
  4. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  5. package/src/transpiler/logic/parser/grammar/CNextParser.ts +992 -870
  6. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  7. package/src/transpiler/logic/symbols/cnext/__tests__/FunctionCollector.test.ts +6 -6
  8. package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolAdapter.test.ts +179 -0
  9. package/src/transpiler/logic/symbols/cnext/__tests__/VariableCollector.test.ts +55 -0
  10. package/src/transpiler/logic/symbols/cnext/adapters/TSymbolAdapter.ts +76 -8
  11. package/src/transpiler/logic/symbols/cnext/collectors/FunctionCollector.ts +9 -10
  12. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +7 -1
  13. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +33 -14
  14. package/src/transpiler/output/codegen/CodeGenerator.ts +243 -166
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +1086 -0
  16. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +254 -22
  17. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +17 -9
  18. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +5 -3
  19. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +12 -7
  20. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +624 -12
  21. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +819 -0
  22. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +337 -0
  23. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +135 -0
  24. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +4 -0
  25. package/src/transpiler/output/codegen/helpers/VariableDeclarationFormatter.ts +118 -0
  26. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +426 -0
  27. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +315 -0
  28. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclarationFormatter.test.ts +333 -0
  29. package/src/transpiler/output/codegen/types/IParameterInput.ts +58 -0
  30. package/src/transpiler/output/codegen/types/IVariableFormatInput.ts +51 -0
  31. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +20 -35
  32. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +21 -48
  33. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +0 -64
@@ -89,6 +89,7 @@ import { TemplateArgumentListContext } from "./CNextParser.js";
89
89
  import { TemplateArgumentContext } from "./CNextParser.js";
90
90
  import { StringTypeContext } from "./CNextParser.js";
91
91
  import { ArrayTypeContext } from "./CNextParser.js";
92
+ import { ArrayTypeDimensionContext } from "./CNextParser.js";
92
93
  import { LiteralContext } from "./CNextParser.js";
93
94
 
94
95
 
@@ -616,6 +617,12 @@ export class CNextVisitor<Result> extends AbstractParseTreeVisitor<Result> {
616
617
  * @return the visitor result
617
618
  */
618
619
  visitArrayType?: (ctx: ArrayTypeContext) => Result;
620
+ /**
621
+ * Visit a parse tree produced by `CNextParser.arrayTypeDimension`.
622
+ * @param ctx the parse tree
623
+ * @return the visitor result
624
+ */
625
+ visitArrayTypeDimension?: (ctx: ArrayTypeDimensionContext) => Result;
619
626
  /**
620
627
  * Visit a parse tree produced by `CNextParser.literal`.
621
628
  * @param ctx the parse tree
@@ -77,9 +77,9 @@ describe("FunctionCollector", () => {
77
77
  expect(symbol.parameters[0].isConst).toBe(true);
78
78
  });
79
79
 
80
- it("handles array parameters", () => {
80
+ it("handles array parameters (C-Next style)", () => {
81
81
  const code = `
82
- void processArray(u8 data[]) {
82
+ void processArray(u8[] data) {
83
83
  }
84
84
  `;
85
85
  const tree = parse(code);
@@ -90,9 +90,9 @@ describe("FunctionCollector", () => {
90
90
  expect(symbol.parameters[0].arrayDimensions).toEqual([""]);
91
91
  });
92
92
 
93
- it("handles sized array parameters", () => {
93
+ it("handles sized array parameters (C-Next style)", () => {
94
94
  const code = `
95
- void processBuffer(u8 buffer[256]) {
95
+ void processBuffer(u8[256] buffer) {
96
96
  }
97
97
  `;
98
98
  const tree = parse(code);
@@ -103,9 +103,9 @@ describe("FunctionCollector", () => {
103
103
  expect(symbol.parameters[0].arrayDimensions).toEqual(["256"]);
104
104
  });
105
105
 
106
- it("handles multi-dimensional array parameters", () => {
106
+ it("handles multi-dimensional array parameters (C-Next style)", () => {
107
107
  const code = `
108
- void processMatrix(f32 matrix[4][4]) {
108
+ void processMatrix(f32[4][4] matrix) {
109
109
  }
110
110
  `;
111
111
  const tree = parse(code);
@@ -463,4 +463,183 @@ describe("TSymbolAdapter", () => {
463
463
  expect(symbolTable.getStructFieldType("Point", "x")).toBe("i32");
464
464
  });
465
465
  });
466
+
467
+ describe("enum member resolution in array dimensions", () => {
468
+ it("resolves unqualified enum member in variable array dimension", () => {
469
+ const enumSym: IEnumSymbol = {
470
+ kind: ESymbolKind.Enum,
471
+ name: "EColor",
472
+ sourceFile: "test.cnx",
473
+ sourceLine: 1,
474
+ sourceLanguage: ESourceLanguage.CNext,
475
+ isExported: true,
476
+ members: new Map([
477
+ ["RED", 0],
478
+ ["GREEN", 1],
479
+ ["BLUE", 2],
480
+ ["COUNT", 3],
481
+ ]),
482
+ };
483
+
484
+ const variable: IVariableSymbol = {
485
+ kind: ESymbolKind.Variable,
486
+ name: "DATA",
487
+ sourceFile: "test.cnx",
488
+ sourceLine: 10,
489
+ sourceLanguage: ESourceLanguage.CNext,
490
+ isExported: true,
491
+ type: "u8",
492
+ isConst: true,
493
+ isAtomic: false,
494
+ isArray: true,
495
+ arrayDimensions: ["COUNT"], // Unqualified enum member
496
+ };
497
+
498
+ const result = TSymbolAdapter.toISymbols(
499
+ [enumSym, variable],
500
+ symbolTable,
501
+ );
502
+
503
+ // Find the variable symbol
504
+ const varSym = result.find(
505
+ (s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
506
+ );
507
+ expect(varSym).toBeDefined();
508
+ expect(varSym!.arrayDimensions).toEqual(["EColor_COUNT"]);
509
+ });
510
+
511
+ it("resolves enum member in function parameter array dimension", () => {
512
+ const enumSym: IEnumSymbol = {
513
+ kind: ESymbolKind.Enum,
514
+ name: "Size",
515
+ sourceFile: "test.cnx",
516
+ sourceLine: 1,
517
+ sourceLanguage: ESourceLanguage.CNext,
518
+ isExported: true,
519
+ members: new Map([
520
+ ["SMALL", 10],
521
+ ["MEDIUM", 20],
522
+ ["LARGE", 30],
523
+ ]),
524
+ };
525
+
526
+ const funcSym: IFunctionSymbol = {
527
+ kind: ESymbolKind.Function,
528
+ name: "process",
529
+ sourceFile: "test.cnx",
530
+ sourceLine: 5,
531
+ sourceLanguage: ESourceLanguage.CNext,
532
+ isExported: true,
533
+ returnType: "void",
534
+ visibility: "public",
535
+ parameters: [
536
+ {
537
+ name: "buffer",
538
+ type: "u8",
539
+ isConst: false,
540
+ isArray: true,
541
+ arrayDimensions: ["MEDIUM"], // Unqualified enum member
542
+ },
543
+ ],
544
+ };
545
+
546
+ const result = TSymbolAdapter.toISymbols([enumSym, funcSym], symbolTable);
547
+
548
+ // Find the function symbol
549
+ const funcResult = result.find(
550
+ (s) => s.kind === ESymbolKind.Function && s.name === "process",
551
+ );
552
+ expect(funcResult).toBeDefined();
553
+ expect(funcResult!.parameters).toBeDefined();
554
+ expect(funcResult!.parameters![0].arrayDimensions).toEqual([
555
+ "Size_MEDIUM",
556
+ ]);
557
+ });
558
+
559
+ it("does not resolve ambiguous enum member (exists in multiple enums)", () => {
560
+ const enum1: IEnumSymbol = {
561
+ kind: ESymbolKind.Enum,
562
+ name: "EColor",
563
+ sourceFile: "test.cnx",
564
+ sourceLine: 1,
565
+ sourceLanguage: ESourceLanguage.CNext,
566
+ isExported: true,
567
+ members: new Map([["COUNT", 3]]),
568
+ };
569
+
570
+ const enum2: IEnumSymbol = {
571
+ kind: ESymbolKind.Enum,
572
+ name: "ESize",
573
+ sourceFile: "test.cnx",
574
+ sourceLine: 5,
575
+ sourceLanguage: ESourceLanguage.CNext,
576
+ isExported: true,
577
+ members: new Map([
578
+ ["COUNT", 5], // Same member name in different enum
579
+ ]),
580
+ };
581
+
582
+ const variable: IVariableSymbol = {
583
+ kind: ESymbolKind.Variable,
584
+ name: "DATA",
585
+ sourceFile: "test.cnx",
586
+ sourceLine: 10,
587
+ sourceLanguage: ESourceLanguage.CNext,
588
+ isExported: true,
589
+ type: "u8",
590
+ isConst: true,
591
+ isAtomic: false,
592
+ isArray: true,
593
+ arrayDimensions: ["COUNT"], // Ambiguous - exists in both enums
594
+ };
595
+
596
+ const result = TSymbolAdapter.toISymbols(
597
+ [enum1, enum2, variable],
598
+ symbolTable,
599
+ );
600
+
601
+ // Find the variable symbol - dimension should NOT be resolved
602
+ const varSym = result.find(
603
+ (s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
604
+ );
605
+ expect(varSym).toBeDefined();
606
+ expect(varSym!.arrayDimensions).toEqual(["COUNT"]); // Left as-is
607
+ });
608
+
609
+ it("preserves numeric array dimensions", () => {
610
+ const enumSym: IEnumSymbol = {
611
+ kind: ESymbolKind.Enum,
612
+ name: "EColor",
613
+ sourceFile: "test.cnx",
614
+ sourceLine: 1,
615
+ sourceLanguage: ESourceLanguage.CNext,
616
+ isExported: true,
617
+ members: new Map([["COUNT", 3]]),
618
+ };
619
+
620
+ const variable: IVariableSymbol = {
621
+ kind: ESymbolKind.Variable,
622
+ name: "DATA",
623
+ sourceFile: "test.cnx",
624
+ sourceLine: 10,
625
+ sourceLanguage: ESourceLanguage.CNext,
626
+ isExported: true,
627
+ type: "u8",
628
+ isConst: true,
629
+ isAtomic: false,
630
+ isArray: true,
631
+ arrayDimensions: [256], // Numeric dimension
632
+ };
633
+
634
+ const result = TSymbolAdapter.toISymbols(
635
+ [enumSym, variable],
636
+ symbolTable,
637
+ );
638
+
639
+ const varSym = result.find(
640
+ (s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
641
+ );
642
+ expect(varSym!.arrayDimensions).toEqual(["256"]);
643
+ });
644
+ });
466
645
  });
@@ -156,6 +156,61 @@ describe("VariableCollector", () => {
156
156
  expect(symbol.isArray).toBe(true);
157
157
  expect(symbol.arrayDimensions).toEqual([10, 20]);
158
158
  });
159
+
160
+ it("collects C-Next style array with dimensions in type (u8[8] arr)", () => {
161
+ const code = `
162
+ u8[8] buffer;
163
+ `;
164
+ const tree = parse(code);
165
+ const varCtx = tree.declaration(0)!.variableDeclaration()!;
166
+ const symbol = VariableCollector.collect(varCtx, "test.cnx");
167
+
168
+ expect(symbol.isArray).toBe(true);
169
+ expect(symbol.arrayDimensions).toEqual([8]);
170
+ });
171
+
172
+ it("collects C-Next style multi-dimensional array (u8[4][4] arr)", () => {
173
+ const code = `
174
+ u8[4][4] matrix;
175
+ `;
176
+ const tree = parse(code);
177
+ const varCtx = tree.declaration(0)!.variableDeclaration()!;
178
+ const symbol = VariableCollector.collect(varCtx, "test.cnx");
179
+
180
+ expect(symbol.isArray).toBe(true);
181
+ expect(symbol.arrayDimensions).toEqual([4, 4]);
182
+ });
183
+
184
+ it("collects C-Next style array with const reference dimension", () => {
185
+ const code = `
186
+ u8[SIZE] buffer;
187
+ `;
188
+ const tree = parse(code);
189
+ const varCtx = tree.declaration(0)!.variableDeclaration()!;
190
+ const constValues = new Map<string, number>([["SIZE", 16]]);
191
+ const symbol = VariableCollector.collect(
192
+ varCtx,
193
+ "test.cnx",
194
+ undefined,
195
+ true,
196
+ constValues,
197
+ );
198
+
199
+ expect(symbol.isArray).toBe(true);
200
+ expect(symbol.arrayDimensions).toEqual([16]);
201
+ });
202
+
203
+ it("preserves unresolved macro as string in C-Next style array", () => {
204
+ const code = `
205
+ u8[BUFFER_SIZE] buffer;
206
+ `;
207
+ const tree = parse(code);
208
+ const varCtx = tree.declaration(0)!.variableDeclaration()!;
209
+ const symbol = VariableCollector.collect(varCtx, "test.cnx");
210
+
211
+ expect(symbol.isArray).toBe(true);
212
+ expect(symbol.arrayDimensions).toEqual(["BUFFER_SIZE"]);
213
+ });
159
214
  });
160
215
 
161
216
  describe("scoped variables", () => {
@@ -40,6 +40,9 @@ class TSymbolAdapter {
40
40
  * @returns Array of flat ISymbol objects for Pipeline consumption
41
41
  */
42
42
  static toISymbols(symbols: TSymbol[], symbolTable: SymbolTable): ISymbol[] {
43
+ // First pass: Build enum member lookup for array dimension resolution
44
+ const enumMemberLookup = TSymbolAdapter.buildEnumMemberLookup(symbols);
45
+
43
46
  const result: ISymbol[] = [];
44
47
 
45
48
  for (const symbol of symbols) {
@@ -54,10 +57,12 @@ class TSymbolAdapter {
54
57
  result.push(TSymbolAdapter.convertStruct(symbol, symbolTable));
55
58
  break;
56
59
  case ESymbolKind.Function:
57
- result.push(...TSymbolAdapter.convertFunction(symbol));
60
+ result.push(
61
+ ...TSymbolAdapter.convertFunction(symbol, enumMemberLookup),
62
+ );
58
63
  break;
59
64
  case ESymbolKind.Variable:
60
- result.push(TSymbolAdapter.convertVariable(symbol));
65
+ result.push(TSymbolAdapter.convertVariable(symbol, enumMemberLookup));
61
66
  break;
62
67
  case ESymbolKind.Register:
63
68
  result.push(...TSymbolAdapter.convertRegister(symbol));
@@ -71,6 +76,57 @@ class TSymbolAdapter {
71
76
  return result;
72
77
  }
73
78
 
79
+ /**
80
+ * Build a lookup map from enum member names to their fully-qualified enum names.
81
+ * Used to resolve unqualified enum members in array dimensions.
82
+ *
83
+ * If a member name exists in multiple enums, it's marked as ambiguous (null value)
84
+ * and will not be resolved (the header will fail to compile, surfacing the issue).
85
+ */
86
+ private static buildEnumMemberLookup(
87
+ symbols: TSymbol[],
88
+ ): Map<string, string | null> {
89
+ const lookup = new Map<string, string | null>();
90
+
91
+ for (const symbol of symbols) {
92
+ if (symbol.kind !== ESymbolKind.Enum) continue;
93
+
94
+ for (const memberName of symbol.members.keys()) {
95
+ if (lookup.has(memberName)) {
96
+ // Ambiguous: member exists in multiple enums
97
+ lookup.set(memberName, null);
98
+ } else {
99
+ lookup.set(memberName, symbol.name);
100
+ }
101
+ }
102
+ }
103
+
104
+ return lookup;
105
+ }
106
+
107
+ /**
108
+ * Resolve an array dimension string, adding enum prefix if it's an unqualified enum member.
109
+ */
110
+ private static resolveArrayDimension(
111
+ dim: number | string,
112
+ enumMemberLookup: Map<string, string | null>,
113
+ ): string {
114
+ // Numeric dimensions don't need resolution
115
+ if (typeof dim === "number") {
116
+ return String(dim);
117
+ }
118
+
119
+ // Check if this is an unqualified enum member
120
+ const enumName = enumMemberLookup.get(dim);
121
+ if (enumName) {
122
+ // Found in exactly one enum - add the prefix
123
+ return `${enumName}_${dim}`;
124
+ }
125
+
126
+ // Not an enum member, or ambiguous - return as-is
127
+ return dim;
128
+ }
129
+
74
130
  /**
75
131
  * Convert IBitmapSymbol to ISymbol + BitmapField symbols.
76
132
  */
@@ -180,21 +236,27 @@ class TSymbolAdapter {
180
236
 
181
237
  /**
182
238
  * Convert IFunctionSymbol to ISymbol + parameter symbols for hover support.
239
+ * Resolves unqualified enum members in parameter array dimensions.
183
240
  */
184
- private static convertFunction(func: IFunctionSymbol): ISymbol[] {
241
+ private static convertFunction(
242
+ func: IFunctionSymbol,
243
+ enumMemberLookup: Map<string, string | null>,
244
+ ): ISymbol[] {
185
245
  const result: ISymbol[] = [];
186
246
 
187
247
  // Build parameter types for signature
188
248
  const paramTypes = func.parameters.map((p) => p.type);
189
249
  const signature = `${func.returnType} ${func.name}(${paramTypes.join(", ")})`;
190
250
 
191
- // Build parameter info for header generation
251
+ // Build parameter info for header generation, resolving enum members in dimensions
192
252
  const parameters = func.parameters.map((p) => ({
193
253
  name: p.name,
194
254
  type: p.type,
195
255
  isConst: p.isConst,
196
256
  isArray: p.isArray,
197
- arrayDimensions: p.arrayDimensions,
257
+ arrayDimensions: p.arrayDimensions?.map((dim) =>
258
+ TSymbolAdapter.resolveArrayDimension(dim, enumMemberLookup),
259
+ ),
198
260
  isAutoConst: p.isAutoConst,
199
261
  }));
200
262
 
@@ -233,10 +295,16 @@ class TSymbolAdapter {
233
295
 
234
296
  /**
235
297
  * Convert IVariableSymbol to ISymbol.
298
+ * Resolves unqualified enum members in array dimensions.
236
299
  */
237
- private static convertVariable(variable: IVariableSymbol): ISymbol {
238
- // Convert dimensions to string dimensions for ISymbol
239
- const arrayDimensions = variable.arrayDimensions?.map(String);
300
+ private static convertVariable(
301
+ variable: IVariableSymbol,
302
+ enumMemberLookup: Map<string, string | null>,
303
+ ): ISymbol {
304
+ // Convert dimensions to string dimensions, resolving enum member references
305
+ const arrayDimensions = variable.arrayDimensions?.map((dim) =>
306
+ TSymbolAdapter.resolveArrayDimension(dim, enumMemberLookup),
307
+ );
240
308
 
241
309
  // Get first dimension for legacy size field (only if numeric)
242
310
  const firstDim = variable.arrayDimensions?.[0];
@@ -70,17 +70,16 @@ class FunctionCollector {
70
70
  const type = TypeUtils.getTypeName(typeCtx, scopeName);
71
71
  const isConst = p.constModifier() !== null;
72
72
 
73
- const arrayDims = p.arrayDimension();
74
- const isArray = arrayDims.length > 0;
73
+ // Check for C-Next style array type (u8[8] param, u8[4][4] param, u8[] param)
74
+ const arrayTypeCtx = typeCtx.arrayType();
75
+ const hasArrayType = arrayTypeCtx !== null;
75
76
 
76
- // Extract array dimensions as strings (can contain expressions like SIZE)
77
+ // Extract array dimensions from arrayType syntax (supports multi-dimensional)
77
78
  const arrayDimensions: string[] = [];
78
- if (isArray) {
79
- for (const dim of arrayDims) {
80
- const text = dim.getText();
81
- const regex = /\[([^\]]*)\]/;
82
- const match = regex.exec(text);
83
- arrayDimensions.push(match ? match[1] : ""); // "" means unbounded
79
+ if (hasArrayType) {
80
+ for (const dim of arrayTypeCtx.arrayTypeDimension()) {
81
+ const sizeExpr = dim.expression();
82
+ arrayDimensions.push(sizeExpr ? sizeExpr.getText() : "");
84
83
  }
85
84
  }
86
85
 
@@ -88,7 +87,7 @@ class FunctionCollector {
88
87
  name,
89
88
  type,
90
89
  isConst,
91
- isArray,
90
+ isArray: hasArrayType,
92
91
  };
93
92
 
94
93
  if (arrayDimensions.length > 0) {
@@ -30,7 +30,13 @@ function processArrayTypeSyntax(
30
30
  return { isArray: false, dimension: undefined };
31
31
  }
32
32
 
33
- const sizeExpr = arrayTypeCtx.expression();
33
+ // Get the first dimension (for backwards compatibility with single-dimension code)
34
+ const dims = arrayTypeCtx.arrayTypeDimension();
35
+ if (dims.length === 0) {
36
+ return { isArray: true, dimension: undefined };
37
+ }
38
+
39
+ const sizeExpr = dims[0].expression();
34
40
  if (!sizeExpr) {
35
41
  return { isArray: true, dimension: undefined };
36
42
  }
@@ -71,6 +71,32 @@ class VariableCollector {
71
71
  return dimensions;
72
72
  }
73
73
 
74
+ /**
75
+ * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr).
76
+ */
77
+ private static collectArrayTypeDimensions(
78
+ arrayTypeCtx: Parser.ArrayTypeContext,
79
+ constValues: Map<string, number> | undefined,
80
+ ): (number | string)[] {
81
+ const dimensions: (number | string)[] = [];
82
+ for (const dim of arrayTypeCtx.arrayTypeDimension()) {
83
+ const sizeExpr = dim.expression();
84
+ if (!sizeExpr) continue;
85
+
86
+ const dimText = sizeExpr.getText();
87
+ const literalSize = LiteralUtils.parseIntegerLiteral(dimText);
88
+ if (literalSize !== undefined) {
89
+ dimensions.push(literalSize);
90
+ } else if (constValues?.has(dimText)) {
91
+ dimensions.push(constValues.get(dimText)!);
92
+ } else {
93
+ // Keep as string for macro/enum references
94
+ dimensions.push(dimText);
95
+ }
96
+ }
97
+ return dimensions;
98
+ }
99
+
74
100
  /**
75
101
  * Collect a variable declaration and return an IVariableSymbol.
76
102
  *
@@ -110,21 +136,14 @@ class VariableCollector {
110
136
  const initExpr = ctx.expression();
111
137
  const arrayDimensions: (number | string)[] = [];
112
138
 
113
- // Collect dimension from arrayType syntax (u16[8] arr)
139
+ // Collect dimensions from arrayType syntax (u16[8] arr, u16[4][4] arr, u16[] arr)
114
140
  if (hasArrayTypeSyntax) {
115
- const sizeExpr = arrayTypeCtx.expression();
116
- if (sizeExpr) {
117
- const dimText = sizeExpr.getText();
118
- const literalSize = LiteralUtils.parseIntegerLiteral(dimText);
119
- if (literalSize !== undefined) {
120
- arrayDimensions.push(literalSize);
121
- } else if (constValues?.has(dimText)) {
122
- arrayDimensions.push(constValues.get(dimText)!);
123
- } else {
124
- // Keep as string for macro/enum references
125
- arrayDimensions.push(dimText);
126
- }
127
- }
141
+ arrayDimensions.push(
142
+ ...VariableCollector.collectArrayTypeDimensions(
143
+ arrayTypeCtx,
144
+ constValues,
145
+ ),
146
+ );
128
147
  }
129
148
 
130
149
  // Collect additional dimensions from arrayDimension syntax