c-next 0.2.15 → 0.2.17

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 (67) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +1403 -427
  3. package/dist/index.js.map +3 -3
  4. package/grammar/CNext.g4 +4 -0
  5. package/package.json +5 -1
  6. package/src/transpiler/Transpiler.ts +90 -22
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
  9. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  10. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  11. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  12. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  13. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  14. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
  15. package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
  16. package/src/transpiler/logic/symbols/SymbolTable.ts +54 -12
  17. package/src/transpiler/logic/symbols/SymbolUtils.ts +21 -8
  18. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +39 -4
  19. package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +2 -1
  20. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +2 -1
  21. package/src/transpiler/logic/symbols/cnext/__tests__/CNextResolver.integration.test.ts +5 -2
  22. package/src/transpiler/logic/symbols/cnext/__tests__/ScopeCollector.test.ts +5 -2
  23. package/src/transpiler/logic/symbols/cnext/collectors/ScopeCollector.ts +7 -2
  24. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  25. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  26. package/src/transpiler/logic/symbols/cpp/utils/DeclaratorUtils.ts +4 -2
  27. package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
  28. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +167 -18
  29. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
  30. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  31. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +32 -8
  33. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +91 -1
  34. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  35. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
  36. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  37. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  38. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  39. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  40. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  41. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
  42. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  43. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  44. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  45. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  46. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  47. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
  49. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  50. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  51. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  52. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  53. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  54. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  55. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
  56. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  57. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  58. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  59. package/src/transpiler/state/CodeGenState.ts +71 -6
  60. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  61. package/src/transpiler/types/symbols/c/ICFieldInfo.ts +6 -2
  62. package/src/transpiler/types/symbols/cpp/ICppFieldInfo.ts +5 -2
  63. package/src/utils/LiteralUtils.ts +23 -0
  64. package/src/utils/ScopeUtils.ts +19 -0
  65. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  66. package/src/utils/__tests__/ScopeUtils.test.ts +10 -0
  67. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -14,20 +14,33 @@ const RESERVED_FIELD_NAMES = new Set<string>([]);
14
14
 
15
15
  /**
16
16
  * Parse array dimensions from declarator text using regex.
17
- * Extracts numeric values from square bracket notation like [8] or [32].
17
+ * Extracts both numeric values and macro names from square bracket notation.
18
+ *
19
+ * Issue #981: Support macro-sized array dimensions like [BUF_SIZE].
20
+ * Without this, struct fields with macro-sized arrays are incorrectly treated
21
+ * as non-arrays, causing bit extraction instead of array access.
18
22
  *
19
23
  * @param text - The declarator text containing array notation
20
- * @returns Array of dimension sizes, e.g., [8] returns [8], [2][3] returns [2, 3]
24
+ * @returns Array of dimension sizes (numbers) or macro names (strings),
25
+ * e.g., [8] returns [8], [BUF_SIZE] returns ["BUF_SIZE"], [2][N] returns [2, "N"]
21
26
  */
22
- function parseArrayDimensions(text: string): number[] {
23
- const dimensions: number[] = [];
24
- const arrayMatches = text.match(/\[(\d+)\]/g);
27
+ function parseArrayDimensions(text: string): (number | string)[] {
28
+ const dimensions: (number | string)[] = [];
29
+ // Match any bracket content: digits, identifiers, or expressions
30
+ // Captures: [8], [BUF_SIZE], [N], [SIZE + 1], etc.
31
+ const arrayMatches = text.match(/\[([^\]]+)\]/g);
25
32
 
26
33
  if (arrayMatches) {
27
34
  for (const match of arrayMatches) {
28
- const size = Number.parseInt(match.slice(1, -1), 10);
29
- if (!Number.isNaN(size)) {
30
- dimensions.push(size);
35
+ const content = match.slice(1, -1).trim();
36
+ // Try to parse as numeric first
37
+ const numericValue = Number.parseInt(content, 10);
38
+ if (!Number.isNaN(numericValue) && String(numericValue) === content) {
39
+ // Pure numeric dimension
40
+ dimensions.push(numericValue);
41
+ } else {
42
+ // Macro name or expression - store as string
43
+ dimensions.push(content);
31
44
  }
32
45
  }
33
46
  }
@@ -658,6 +658,39 @@ describe("SymbolTable", () => {
658
658
  const fields = symbolTable.getStructFields("Point");
659
659
  expect(fields?.size).toBe(2);
660
660
  });
661
+
662
+ it("Issue #981: addCSymbol should register struct fields with macro dimensions", () => {
663
+ // When a C struct with macro-sized array fields is added via addCSymbol,
664
+ // the fields should be registered in structFields for getMemberTypeInfo lookups
665
+ const cStructSymbol: TCSymbol = {
666
+ kind: "struct",
667
+ name: "msg_t",
668
+ sourceFile: "fake_lib.h",
669
+ sourceLine: 5,
670
+ sourceLanguage: ESourceLanguage.C,
671
+ isExported: true,
672
+ isUnion: false,
673
+ fields: new Map([
674
+ [
675
+ "data",
676
+ { name: "data", type: "uint8_t", arrayDimensions: ["BUF_SIZE"] },
677
+ ],
678
+ ]),
679
+ };
680
+
681
+ symbolTable.addCSymbol(cStructSymbol);
682
+
683
+ // Struct fields should now be registered
684
+ const fields = symbolTable.getStructFields("msg_t");
685
+ expect(fields).toBeDefined();
686
+ expect(fields?.size).toBe(1);
687
+
688
+ // Field info should include the macro-sized array dimension
689
+ const dataField = symbolTable.getStructFieldInfo("msg_t", "data");
690
+ expect(dataField).toBeDefined();
691
+ expect(dataField?.type).toBe("uint8_t");
692
+ expect(dataField?.arrayDimensions).toEqual(["BUF_SIZE"]);
693
+ });
661
694
  });
662
695
 
663
696
  // ========================================================================
@@ -752,14 +785,16 @@ describe("SymbolTable", () => {
752
785
  expect(symbolTable.isTypedefStructType("other_t")).toBe(false);
753
786
  });
754
787
 
755
- it("should always return true for typedef struct types (additive only)", () => {
756
- // Issue #958: typedef struct types are never unmarked
788
+ it("should return false when underlying struct tag has body (issue #948)", () => {
789
+ // Issue #948: Query-time resolution - if the underlying struct tag
790
+ // has a full body definition, this is NOT an external typedef struct
757
791
  symbolTable.markTypedefStructType("point_t", "point.h");
758
792
  expect(symbolTable.isTypedefStructType("point_t")).toBe(true);
759
- // Even after marking the body, typedef struct type stays marked
793
+ // After marking the body, typedef struct type should return false
794
+ // because it's now a complete type (value semantics, not pointer)
760
795
  symbolTable.registerStructTagAlias("_point", "point_t");
761
796
  symbolTable.markStructTagHasBody("_point");
762
- expect(symbolTable.isTypedefStructType("point_t")).toBe(true);
797
+ expect(symbolTable.isTypedefStructType("point_t")).toBe(false);
763
798
  });
764
799
 
765
800
  it("should get all typedef struct types", () => {
@@ -33,9 +33,10 @@ describe("SymbolUtils", () => {
33
33
  expect(SymbolUtils.parseArrayDimensions("uint8_t data[10];")).toEqual([
34
34
  10,
35
35
  ]);
36
+ // Issue #981: macro-sized arrays are captured as strings
36
37
  expect(
37
38
  SymbolUtils.parseArrayDimensions("char buffer[BUFFER_SIZE]"),
38
- ).toEqual([]);
39
+ ).toEqual(["BUFFER_SIZE"]);
39
40
  });
40
41
  });
41
42
 
@@ -131,8 +131,9 @@ class DeclaratorUtils {
131
131
 
132
132
  /**
133
133
  * Extract array dimensions from a declarator.
134
+ * Issue #981: Returns (number | string)[] to support macro-sized arrays.
134
135
  */
135
- static extractArrayDimensions(declarator: any): number[] {
136
+ static extractArrayDimensions(declarator: any): (number | string)[] {
136
137
  const directDecl = declarator.directDeclarator?.();
137
138
  if (!directDecl) return [];
138
139
 
@@ -95,14 +95,15 @@ describe("CNextResolver Integration", () => {
95
95
 
96
96
  describe("scope handling", () => {
97
97
  it("resolves scope with members", () => {
98
+ // ADR-016: Functions are public by default, variables are private by default
98
99
  const code = `
99
100
  scope Motor {
100
101
  u32 position;
101
102
 
102
- public void init() {
103
+ void init() {
103
104
  }
104
105
 
105
- void update() {
106
+ private void update() {
106
107
  }
107
108
  }
108
109
  `;
@@ -122,6 +123,7 @@ describe("CNextResolver Integration", () => {
122
123
  expect(SymbolGuards.isVariable(positionVar!)).toBe(true);
123
124
  expect(positionVar!.scope.name).toBe("Motor");
124
125
 
126
+ // init() is public by default (no modifier needed)
125
127
  const initFunc = symbols.find((s) => s.name === "init");
126
128
  expect(initFunc).toBeDefined();
127
129
  if (SymbolGuards.isFunction(initFunc!)) {
@@ -129,6 +131,7 @@ describe("CNextResolver Integration", () => {
129
131
  expect(initFunc.scope.name).toBe("Motor");
130
132
  }
131
133
 
134
+ // update() has explicit 'private' keyword
132
135
  const updateFunc = symbols.find((s) => s.name === "update");
133
136
  expect(updateFunc).toBeDefined();
134
137
  if (SymbolGuards.isFunction(updateFunc!)) {
@@ -32,11 +32,12 @@ describe("ScopeCollector", () => {
32
32
  });
33
33
 
34
34
  it("collects scope with functions", () => {
35
+ // ADR-016: Functions are public by default, use explicit 'private' for internal functions
35
36
  const code = `
36
37
  scope Motor {
37
- public void init() {
38
+ void init() {
38
39
  }
39
- void update() {
40
+ private void update() {
40
41
  }
41
42
  }
42
43
  `;
@@ -45,7 +46,9 @@ describe("ScopeCollector", () => {
45
46
  const result = ScopeCollector.collect(scopeCtx, "test.cnx", new Set());
46
47
 
47
48
  expect(result.scopeSymbol.members).toEqual(["init", "update"]);
49
+ // init() is public by default (no modifier)
48
50
  expect(result.scopeSymbol.memberVisibility.get("init")).toBe("public");
51
+ // update() has explicit 'private' keyword
49
52
  expect(result.scopeSymbol.memberVisibility.get("update")).toBe("private");
50
53
 
51
54
  expect(result.memberSymbols.length).toBe(2);
@@ -8,6 +8,7 @@
8
8
 
9
9
  import * as Parser from "../../../parser/grammar/CNextParser";
10
10
  import ESourceLanguage from "../../../../../utils/types/ESourceLanguage";
11
+ import ScopeUtils from "../../../../../utils/ScopeUtils";
11
12
  import TSymbol from "../../../../types/symbols/TSymbol";
12
13
  import TVisibility from "../../../../types/TVisibility";
13
14
  import IScopeCollectorResult from "../types/IScopeCollectorResult";
@@ -69,10 +70,14 @@ class ScopeCollector {
69
70
  const memberSymbols: TSymbol[] = [];
70
71
 
71
72
  for (const member of ctx.scopeMember()) {
72
- // ADR-016: Extract visibility (private by default)
73
+ // ADR-016: Extract visibility with member-type-aware defaults
73
74
  const visibilityMod = member.visibilityModifier();
75
+ const explicitVisibility = visibilityMod?.getText() as
76
+ | TVisibility
77
+ | undefined;
78
+ const isFunction = member.functionDeclaration() !== null;
74
79
  const visibility: TVisibility =
75
- (visibilityMod?.getText() as TVisibility) ?? "private";
80
+ explicitVisibility ?? ScopeUtils.getDefaultVisibility(isFunction);
76
81
  const isPublic = visibility === "public";
77
82
 
78
83
  // Handle variable declarations
@@ -75,16 +75,28 @@ class VariableCollector {
75
75
  }
76
76
 
77
77
  /**
78
- * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr).
78
+ * Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr, u16[] arr).
79
+ * Handles size inference from initializer when dimension is empty.
79
80
  */
80
81
  private static collectArrayTypeDimensions(
81
82
  arrayTypeCtx: Parser.ArrayTypeContext,
82
83
  constValues: Map<string, number> | undefined,
84
+ initExpr: Parser.ExpressionContext | null,
83
85
  ): (number | string)[] {
84
86
  const dimensions: (number | string)[] = [];
85
87
  for (const dim of arrayTypeCtx.arrayTypeDimension()) {
86
88
  const sizeExpr = dim.expression();
87
- if (!sizeExpr) continue;
89
+
90
+ if (!sizeExpr) {
91
+ // Issue #636: Empty dimension [] - infer size from array initializer
92
+ if (initExpr) {
93
+ const inferredSize = ArrayInitializerUtils.getInferredSize(initExpr);
94
+ if (inferredSize !== undefined) {
95
+ dimensions.push(inferredSize);
96
+ }
97
+ }
98
+ continue;
99
+ }
88
100
 
89
101
  const dimText = sizeExpr.getText();
90
102
  const literalSize = LiteralUtils.parseIntegerLiteral(dimText);
@@ -146,6 +158,7 @@ class VariableCollector {
146
158
  ...VariableCollector.collectArrayTypeDimensions(
147
159
  arrayTypeCtx,
148
160
  constValues,
161
+ initExpr,
149
162
  ),
150
163
  );
151
164
  }
@@ -6,14 +6,16 @@ import * as Parser from "../../../parser/grammar/CNextParser";
6
6
  import CNEXT_TO_C_TYPE_MAP from "../../../../../utils/constants/TypeMappings";
7
7
 
8
8
  /**
9
- * Resolve scoped type (this.Type) to full name.
9
+ * Common interface for type contexts that share the same type accessors.
10
+ * Both TypeContext and ArrayTypeContext have these methods.
10
11
  */
11
- function resolveScopedType(
12
- scopedTypeCtx: Parser.ScopedTypeContext,
13
- scopeName?: string,
14
- ): string {
15
- const typeName = scopedTypeCtx.IDENTIFIER().getText();
16
- return scopeName ? `${scopeName}_${typeName}` : typeName;
12
+ interface ITypeAccessors {
13
+ primitiveType(): Parser.PrimitiveTypeContext | null;
14
+ userType(): Parser.UserTypeContext | null;
15
+ stringType(): Parser.StringTypeContext | null;
16
+ scopedType(): Parser.ScopedTypeContext | null;
17
+ qualifiedType(): Parser.QualifiedTypeContext | null;
18
+ globalType(): Parser.GlobalTypeContext | null;
17
19
  }
18
20
 
19
21
  /**
@@ -25,19 +27,50 @@ function resolveStringType(stringCtx: Parser.StringTypeContext): string {
25
27
  }
26
28
 
27
29
  /**
28
- * Extract inner type from arrayType context.
30
+ * Dispatch type resolution for contexts that share common type accessors.
31
+ * Handles scoped, qualified, global, primitive, string, and user types.
32
+ * Used by both bare type contexts and array element type contexts.
33
+ *
34
+ * @returns The resolved type name, or null if no matching type accessor found
29
35
  */
30
- function resolveArrayInnerType(arrayTypeCtx: Parser.ArrayTypeContext): string {
31
- if (arrayTypeCtx.primitiveType()) {
32
- return arrayTypeCtx.primitiveType()!.getText();
36
+ function dispatchTypeResolution(
37
+ accessors: ITypeAccessors,
38
+ scopeName?: string,
39
+ ): string | null {
40
+ // Handle this.Type for scoped types (e.g., this.State -> Motor_State)
41
+ if (accessors.scopedType()) {
42
+ const typeName = accessors.scopedType()!.IDENTIFIER().getText();
43
+ return scopeName ? `${scopeName}_${typeName}` : typeName;
44
+ }
45
+
46
+ // Handle global.Type for global types inside scope
47
+ // global.ECategory -> ECategory (just the type name, no scope prefix)
48
+ if (accessors.globalType()) {
49
+ return accessors.globalType()!.IDENTIFIER().getText();
33
50
  }
34
- if (arrayTypeCtx.userType()) {
35
- return arrayTypeCtx.userType()!.getText();
51
+
52
+ // Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
53
+ if (accessors.qualifiedType()) {
54
+ const identifiers = accessors.qualifiedType()!.IDENTIFIER();
55
+ return identifiers.map((id) => id.getText()).join("_");
56
+ }
57
+
58
+ // Handle user-defined types
59
+ if (accessors.userType()) {
60
+ return accessors.userType()!.getText();
36
61
  }
37
- // Fallback for other nested types - strip the dimension part
38
- const text = arrayTypeCtx.getText();
39
- const bracketIdx = text.indexOf("[");
40
- return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
62
+
63
+ // Handle primitive types
64
+ if (accessors.primitiveType()) {
65
+ return accessors.primitiveType()!.getText();
66
+ }
67
+
68
+ // Handle string types - preserve capacity for validation (Issue #139)
69
+ if (accessors.stringType()) {
70
+ return resolveStringType(accessors.stringType()!);
71
+ }
72
+
73
+ return null;
41
74
  }
42
75
 
43
76
  class TypeUtils {
@@ -56,42 +89,23 @@ class TypeUtils {
56
89
  ): string {
57
90
  if (!ctx) return "void";
58
91
 
59
- // Handle this.Type for scoped types (e.g., this.State -> Motor_State)
60
- if (ctx.scopedType()) {
61
- return resolveScopedType(ctx.scopedType()!, scopeName);
62
- }
63
-
64
- // Issue #478: Handle global.Type for global types inside scope
65
- // global.ECategory -> ECategory (just the type name, no scope prefix)
66
- if (ctx.globalType()) {
67
- return ctx.globalType()!.IDENTIFIER().getText();
68
- }
69
-
70
- // Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
71
- if (ctx.qualifiedType()) {
72
- const identifiers = ctx.qualifiedType()!.IDENTIFIER();
73
- return identifiers.map((id) => id.getText()).join("_");
74
- }
75
-
76
- // Handle user-defined types
77
- if (ctx.userType()) {
78
- return ctx.userType()!.getText();
79
- }
80
-
81
- // Handle primitive types
82
- if (ctx.primitiveType()) {
83
- return ctx.primitiveType()!.getText();
84
- }
85
-
86
- // Handle string types - preserve capacity for validation (Issue #139)
87
- if (ctx.stringType()) {
88
- return resolveStringType(ctx.stringType()!);
89
- }
90
-
91
92
  // Handle arrayType: Type[size] - extract the inner type without dimension
92
93
  // The dimension is tracked separately in arrayDimensions
93
94
  if (ctx.arrayType()) {
94
- return resolveArrayInnerType(ctx.arrayType()!);
95
+ const result = dispatchTypeResolution(ctx.arrayType()!, scopeName);
96
+ if (result !== null) {
97
+ return result;
98
+ }
99
+ // Fallback for unrecognized array types - strip the dimension part
100
+ const text = ctx.arrayType()!.getText();
101
+ const bracketIdx = text.indexOf("[");
102
+ return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
103
+ }
104
+
105
+ // Non-array types - dispatch directly
106
+ const result = dispatchTypeResolution(ctx, scopeName);
107
+ if (result !== null) {
108
+ return result;
95
109
  }
96
110
 
97
111
  // Fallback
@@ -217,8 +217,9 @@ class DeclaratorUtils {
217
217
 
218
218
  /**
219
219
  * Extract array dimensions from a declarator.
220
+ * Issue #981: Returns (number | string)[] to support macro-sized arrays.
220
221
  */
221
- static extractArrayDimensions(declarator: any): number[] {
222
+ static extractArrayDimensions(declarator: any): (number | string)[] {
222
223
  // For C++, we need to check both pointer and no-pointer declarators
223
224
  const ptrDecl = declarator.pointerDeclarator?.();
224
225
  if (ptrDecl) {
@@ -238,8 +239,9 @@ class DeclaratorUtils {
238
239
 
239
240
  /**
240
241
  * Extract array dimensions from a noPointerDeclarator.
242
+ * Issue #981: Returns (number | string)[] to support macro-sized arrays.
241
243
  */
242
- static extractArrayDimensionsFromNoPtr(noPtr: any): number[] {
244
+ static extractArrayDimensionsFromNoPtr(noPtr: any): (number | string)[] {
243
245
  // Use shared utility for regex-based extraction
244
246
  return SymbolUtils.parseArrayDimensions(noPtr.getText());
245
247
  }