c-next 0.1.61 → 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 (104) hide show
  1. package/README.md +86 -63
  2. package/grammar/CNext.g4 +3 -17
  3. package/package.json +1 -1
  4. package/src/cli/serve/ServeCommand.ts +57 -45
  5. package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
  6. package/src/transpiler/Transpiler.ts +603 -613
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
  8. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
  9. package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
  10. package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
  11. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
  12. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
  13. package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
  14. package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
  15. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
  16. package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
  17. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  20. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
  22. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  23. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  24. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  25. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
  26. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  27. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  28. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  29. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  30. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
  31. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
  32. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
  33. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  34. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
  35. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
  36. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
  37. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
  38. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
  39. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
  41. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  42. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
  43. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  44. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  45. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
  46. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
  47. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
  48. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
  49. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  50. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  51. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  52. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
  53. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
  54. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  55. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
  56. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  57. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
  58. package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
  59. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
  60. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  61. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  62. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  63. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
  64. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  65. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
  66. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  67. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
  68. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  69. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  70. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  71. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  72. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  73. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  74. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
  75. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  76. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
  77. package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
  78. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  79. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  80. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  81. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  82. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  83. package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
  84. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
  85. package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
  86. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
  87. package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
  88. package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
  89. package/src/transpiler/types/IFileResult.ts +0 -4
  90. package/src/transpiler/types/IPipelineFile.ts +27 -0
  91. package/src/transpiler/types/IPipelineInput.ts +23 -0
  92. package/src/transpiler/types/TranspilerState.ts +1 -1
  93. package/src/utils/FormatUtils.ts +28 -2
  94. package/src/utils/MapUtils.ts +25 -0
  95. package/src/utils/PostfixAnalysisUtils.ts +48 -0
  96. package/src/utils/__tests__/FormatUtils.test.ts +42 -0
  97. package/src/utils/__tests__/MapUtils.test.ts +85 -0
  98. package/src/utils/constants/OperatorMappings.ts +19 -0
  99. package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
  100. package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
  101. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  102. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
  103. package/src/transpiler/types/ITranspileContext.ts +0 -49
  104. package/src/transpiler/types/ITranspileContribution.ts +0 -32
@@ -158,6 +158,47 @@ function handleArrayElementBit(
158
158
  return `${arrayElement} = (${arrayElement} & ~(${one} << ${bitIndex})) | (${intValue} << ${bitIndex});`;
159
159
  }
160
160
 
161
+ /**
162
+ * Handle bit range through struct chain: devices[0].control[0, 4] <- 15
163
+ *
164
+ * The target is a chain like array[idx].member or struct.field with a
165
+ * bit range subscript [start, width] at the end.
166
+ */
167
+ function handleStructChainBitRange(
168
+ ctx: IAssignmentContext,
169
+ deps: IHandlerDeps,
170
+ ): string {
171
+ validateNotCompound(ctx);
172
+
173
+ // Build the base target from postfixOps, excluding the last one (the bit range)
174
+ const baseId = ctx.identifiers[0];
175
+ const opsBeforeLast = ctx.postfixOps.slice(0, -1);
176
+
177
+ let baseTarget = baseId;
178
+ for (const op of opsBeforeLast) {
179
+ const memberId = op.IDENTIFIER();
180
+ if (memberId) {
181
+ baseTarget += "." + memberId.getText();
182
+ } else {
183
+ const exprs = op.expression();
184
+ if (exprs.length > 0) {
185
+ baseTarget += "[" + deps.generateExpression(exprs[0]) + "]";
186
+ }
187
+ }
188
+ }
189
+
190
+ // Get start and width from the last postfixOp (the bit range)
191
+ const lastOp = ctx.postfixOps.at(-1)!;
192
+ const bitRangeExprs = lastOp.expression();
193
+ const start = deps.generateExpression(bitRangeExprs[0]);
194
+ const width = deps.generateExpression(bitRangeExprs[1]);
195
+
196
+ // Generate bit range write
197
+ // Limitation: assumes 32-bit types. For 64-bit struct members,
198
+ // would need to track member type through chain.
199
+ return BitUtils.multiBitWrite(baseTarget, start, width, ctx.generatedValue);
200
+ }
201
+
161
202
  /**
162
203
  * All bit access handlers for registration.
163
204
  */
@@ -166,6 +207,7 @@ const bitAccessHandlers: ReadonlyArray<[AssignmentKind, TAssignmentHandler]> = [
166
207
  [AssignmentKind.INTEGER_BIT_RANGE, handleIntegerBitRange],
167
208
  [AssignmentKind.STRUCT_MEMBER_BIT, handleStructMemberBit],
168
209
  [AssignmentKind.ARRAY_ELEMENT_BIT, handleArrayElementBit],
210
+ [AssignmentKind.STRUCT_CHAIN_BIT_RANGE, handleStructChainBitRange],
169
211
  ];
170
212
 
171
213
  export default bitAccessHandlers;
@@ -88,10 +88,11 @@ describe("BitAccessHandlers", () => {
88
88
  expect(kinds).toContain(AssignmentKind.INTEGER_BIT_RANGE);
89
89
  expect(kinds).toContain(AssignmentKind.STRUCT_MEMBER_BIT);
90
90
  expect(kinds).toContain(AssignmentKind.ARRAY_ELEMENT_BIT);
91
+ expect(kinds).toContain(AssignmentKind.STRUCT_CHAIN_BIT_RANGE);
91
92
  });
92
93
 
93
- it("exports exactly 4 handlers", () => {
94
- expect(bitAccessHandlers.length).toBe(4);
94
+ it("exports exactly 5 handlers", () => {
95
+ expect(bitAccessHandlers.length).toBe(5);
95
96
  });
96
97
  });
97
98
 
@@ -470,4 +471,77 @@ describe("BitAccessHandlers", () => {
470
471
  );
471
472
  });
472
473
  });
474
+
475
+ describe("handleStructChainBitRange (STRUCT_CHAIN_BIT_RANGE)", () => {
476
+ const getHandler = () =>
477
+ bitAccessHandlers.find(
478
+ ([kind]) => kind === AssignmentKind.STRUCT_CHAIN_BIT_RANGE,
479
+ )?.[1];
480
+
481
+ it("generates bit range write through struct chain", () => {
482
+ const deps = createMockDeps({
483
+ generateExpression: vi
484
+ .fn()
485
+ .mockReturnValueOnce("0") // array index
486
+ .mockReturnValueOnce("0") // bit range start
487
+ .mockReturnValueOnce("4"), // bit range width
488
+ });
489
+
490
+ // Create mock postfixOps for devices[0].control[0, 4]
491
+ const mockPostfixOps = [
492
+ {
493
+ IDENTIFIER: () => null,
494
+ expression: () => [{ mockValue: "0" }],
495
+ },
496
+ {
497
+ IDENTIFIER: () => ({ getText: () => "control" }),
498
+ expression: () => [],
499
+ },
500
+ {
501
+ IDENTIFIER: () => null,
502
+ expression: () => [{ mockValue: "0" }, { mockValue: "4" }],
503
+ },
504
+ ];
505
+
506
+ const ctx = createMockContext({
507
+ identifiers: ["devices", "control"],
508
+ subscripts: [
509
+ { mockValue: "0" } as never,
510
+ { mockValue: "0" } as never,
511
+ { mockValue: "4" } as never,
512
+ ],
513
+ postfixOps: mockPostfixOps as never,
514
+ generatedValue: "15",
515
+ hasMemberAccess: true,
516
+ hasArrayAccess: true,
517
+ lastSubscriptExprCount: 2,
518
+ });
519
+
520
+ const result = getHandler()!(ctx, deps);
521
+
522
+ expect(result).toContain("devices[0].control =");
523
+ expect(result).toContain("& ~(");
524
+ expect(result).toContain("<< 0");
525
+ expect(result).toContain("15");
526
+ });
527
+
528
+ it("throws on compound assignment", () => {
529
+ const deps = createMockDeps();
530
+ const mockPostfixOps = [
531
+ {
532
+ IDENTIFIER: () => null,
533
+ expression: () => [{ mockValue: "0" }, { mockValue: "4" }],
534
+ },
535
+ ];
536
+ const ctx = createMockContext({
537
+ isCompound: true,
538
+ cnextOp: "+<-",
539
+ postfixOps: mockPostfixOps as never,
540
+ });
541
+
542
+ expect(() => getHandler()!(ctx, deps)).toThrow(
543
+ "Compound assignment operators not supported for bit field access",
544
+ );
545
+ });
546
+ });
473
547
  });
@@ -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
  // =========================================================================
@@ -120,7 +120,11 @@ interface IOrchestrator {
120
120
  // === Validation ===
121
121
 
122
122
  /** Validate cross-scope member visibility (ADR-016) */
123
- validateCrossScopeVisibility(scopeName: string, memberName: string): void;
123
+ validateCrossScopeVisibility(
124
+ scopeName: string,
125
+ memberName: string,
126
+ isGlobalAccess?: boolean,
127
+ ): void;
124
128
 
125
129
  /** Validate shift amount is within type bounds */
126
130
  validateShiftAmount(
@@ -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;
@@ -20,6 +20,7 @@ import IGeneratorState from "../IGeneratorState";
20
20
  import IGeneratorOutput from "../IGeneratorOutput";
21
21
  import IOrchestrator from "../IOrchestrator";
22
22
  import TGeneratorFn from "../TGeneratorFn";
23
+ import generateRegisterMacros from "./RegisterMacroGenerator";
23
24
 
24
25
  /**
25
26
  * Generate C #define macros from a C-Next register declaration.
@@ -36,30 +37,16 @@ const generateRegister: TGeneratorFn<Parser.RegisterDeclarationContext> = (
36
37
  const name = node.IDENTIFIER().getText();
37
38
  const baseAddress = orchestrator.generateExpression(node.expression());
38
39
 
39
- const lines: string[] = [];
40
- lines.push(`/* Register: ${name} @ ${baseAddress} */`);
41
-
42
- // Generate individual #define for each register member with its offset
43
- // This handles non-contiguous register layouts correctly (like i.MX RT1062)
44
- for (const member of node.registerMember()) {
45
- const regName = member.IDENTIFIER().getText();
46
- const regType = orchestrator.generateType(member.type());
47
- const access = member.accessModifier().getText();
48
- const offset = orchestrator.generateExpression(member.expression());
49
-
50
- // Determine qualifiers based on access mode
51
- let cast = `volatile ${regType}*`;
52
- if (access === "ro") {
53
- cast = `volatile ${regType} const *`;
54
- }
55
-
56
- // Generate: #define GPIO7_DR (*(volatile uint32_t*)(0x42004000 + 0x00))
57
- lines.push(
58
- `#define ${name}_${regName} (*(${cast})(${baseAddress} + ${offset}))`,
59
- );
60
- }
61
-
62
- lines.push("");
40
+ const lines: string[] = [
41
+ `/* Register: ${name} @ ${baseAddress} */`,
42
+ ...generateRegisterMacros(
43
+ node.registerMember(),
44
+ name,
45
+ baseAddress,
46
+ orchestrator,
47
+ ),
48
+ "",
49
+ ];
63
50
 
64
51
  return {
65
52
  code: lines.join("\n"),
@@ -0,0 +1,64 @@
1
+ /**
2
+ * RegisterMacroGenerator - Shared logic for register #define macro generation
3
+ *
4
+ * Extracts common logic from RegisterGenerator and ScopedRegisterGenerator
5
+ * for generating C #define macros from C-Next register members.
6
+ */
7
+ import * as Parser from "../../../../logic/parser/grammar/CNextParser";
8
+ import IOrchestrator from "../IOrchestrator";
9
+
10
+ /**
11
+ * Optional type resolver for scoped types.
12
+ * Returns resolved type name if found, undefined to use original type.
13
+ */
14
+ type TTypeResolver = (originalType: string) => string | undefined;
15
+
16
+ /**
17
+ * Generate #define macros for register members.
18
+ *
19
+ * @param members - Register member declarations from AST
20
+ * @param prefix - Prefix for macro names (e.g., "GPIO7" or "Teensy4_GPIO7")
21
+ * @param baseAddress - Base address expression string
22
+ * @param orchestrator - Code generation orchestrator
23
+ * @param typeResolver - Optional callback to resolve scoped types
24
+ * @returns Array of #define lines
25
+ */
26
+ function generateRegisterMacros(
27
+ members: Parser.RegisterMemberContext[],
28
+ prefix: string,
29
+ baseAddress: string,
30
+ orchestrator: IOrchestrator,
31
+ typeResolver?: TTypeResolver,
32
+ ): string[] {
33
+ const lines: string[] = [];
34
+
35
+ for (const member of members) {
36
+ const regName = member.IDENTIFIER().getText();
37
+ let regType = orchestrator.generateType(member.type());
38
+ const access = member.accessModifier().getText();
39
+ const offset = orchestrator.generateExpression(member.expression());
40
+
41
+ // Apply optional type resolution (for scoped bitmaps)
42
+ if (typeResolver) {
43
+ const resolved = typeResolver(regType);
44
+ if (resolved) {
45
+ regType = resolved;
46
+ }
47
+ }
48
+
49
+ // Determine qualifiers based on access mode
50
+ let cast = `volatile ${regType}*`;
51
+ if (access === "ro") {
52
+ cast = `volatile ${regType} const *`;
53
+ }
54
+
55
+ // Generate: #define PREFIX_REGNAME (*(volatile type*)(base + offset))
56
+ lines.push(
57
+ `#define ${prefix}_${regName} (*(${cast})(${baseAddress} + ${offset}))`,
58
+ );
59
+ }
60
+
61
+ return lines;
62
+ }
63
+
64
+ export default generateRegisterMacros;
@@ -22,6 +22,34 @@ 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
+ }
41
+
42
+ /**
43
+ * Extract scoped name from a declaration node.
44
+ * Returns both the local name and the fully qualified scoped name.
45
+ */
46
+ function getScopedName(
47
+ node: { IDENTIFIER(): { getText(): string } },
48
+ scopeName: string,
49
+ ): { name: string; fullName: string } {
50
+ const name = node.IDENTIFIER().getText();
51
+ return { name, fullName: `${scopeName}_${name}` };
52
+ }
25
53
 
26
54
  /**
27
55
  * Validate and resolve constructor arguments, ensuring each is const.
@@ -69,30 +97,25 @@ function generateScopeVariable(
69
97
  // Issue #375: Check for constructor syntax
70
98
  const constructorArgList = varDecl.constructorArgumentList();
71
99
  if (constructorArgList) {
72
- // ADR-016: All scope variables are emitted at file scope
73
- const type = orchestrator.generateType(varDecl.type());
74
- const fullName = `${scopeName}_${varName}`;
75
- const prefix = isPrivate ? "static " : "";
76
-
77
- // Validate and resolve constructor arguments
78
- const argIdentifiers = constructorArgList.IDENTIFIER();
79
- const line = varDecl.start?.line ?? 0;
80
- const resolvedArgs = resolveConstructorArgs(
81
- argIdentifiers,
100
+ return generateConstructorVariable(
101
+ varDecl,
102
+ varName,
82
103
  scopeName,
83
- line,
104
+ isPrivate,
105
+ constructorArgList,
84
106
  orchestrator,
85
107
  );
86
-
87
- return `${prefix}${type} ${fullName}(${resolvedArgs.join(", ")});`;
88
108
  }
89
109
 
90
110
  // Issue #282: Check if this is a const variable - const values should be inlined
91
111
  const isConst = varDecl.constModifier() !== null;
92
112
 
93
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
94
116
  const arrayDims = varDecl.arrayDimension();
95
- const isArray = arrayDims.length > 0;
117
+ const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
118
+ const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
96
119
 
97
120
  // Issue #282: Private const variables should be inlined, not emitted at file scope
98
121
  // Issue #500: EXCEPT arrays - arrays must be emitted as static const
@@ -101,6 +124,60 @@ function generateScopeVariable(
101
124
  return null;
102
125
  }
103
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
+
104
181
  // ADR-016: All scope variables are emitted at file scope (static-like persistence)
105
182
  const type = orchestrator.generateType(varDecl.type());
106
183
  const fullName = `${scopeName}_${varName}`;
@@ -108,26 +185,22 @@ function generateScopeVariable(
108
185
  const constPrefix = isConst ? "const " : "";
109
186
  const prefix = isPrivate ? "static " : "";
110
187
 
111
- // ADR-036: arrayDimension() now returns an array (arrayDims defined above)
188
+ // Build declaration with all dimensions
112
189
  let decl = `${prefix}${constPrefix}${type} ${fullName}`;
113
- if (isArray) {
190
+ decl += ArrayDimensionUtils.generateArrayTypeDimension(
191
+ arrayTypeCtx,
192
+ orchestrator,
193
+ );
194
+
195
+ if (arrayDims.length > 0) {
196
+ // C-style or additional dimensions
114
197
  decl += orchestrator.generateArrayDimensions(arrayDims);
115
198
  }
199
+
116
200
  // ADR-045: Add string capacity dimension for string arrays
117
- if (varDecl.type().stringType()) {
118
- const stringCtx = varDecl.type().stringType()!;
119
- const intLiteral = stringCtx.INTEGER_LITERAL();
120
- if (intLiteral) {
121
- const capacity = Number.parseInt(intLiteral.getText(), 10);
122
- decl += `[${capacity + 1}]`;
123
- }
124
- }
125
- if (varDecl.expression()) {
126
- decl += ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
127
- } else {
128
- // ADR-015: Zero initialization for uninitialized scope variables
129
- decl += ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
130
- }
201
+ decl += ArrayDimensionUtils.generateStringCapacityDim(varDecl.type());
202
+ decl += generateInitializer(varDecl, isArray, orchestrator);
203
+
131
204
  return decl + ";";
132
205
  }
133
206
 
@@ -339,11 +412,8 @@ function generateScopedEnumInline(
339
412
  input: IGeneratorInput,
340
413
  orchestrator: IOrchestrator,
341
414
  ): string {
342
- const name = node.IDENTIFIER().getText();
343
- const fullName = `${scopeName}_${name}`;
344
-
345
- const lines: string[] = [];
346
- lines.push(`typedef enum {`);
415
+ const { fullName } = getScopedName(node, scopeName);
416
+ const lines: string[] = [`typedef enum {`];
347
417
 
348
418
  // Try to get members from symbol info first
349
419
  const symbolMembers = input.symbols?.enumMembers.get(fullName);
@@ -449,6 +519,35 @@ function generateScopedBitmapInline(
449
519
  return lines.join("\n");
450
520
  }
451
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
+
452
551
  /**
453
552
  * Generate struct inside a scope with proper prefixing.
454
553
  * Struct fields maintain their original types.
@@ -459,35 +558,12 @@ function generateScopedStructInline(
459
558
  _input: IGeneratorInput,
460
559
  orchestrator: IOrchestrator,
461
560
  ): string {
462
- const name = node.IDENTIFIER().getText();
463
- const fullName = `${scopeName}_${name}`;
464
-
465
- const lines: string[] = [];
466
- lines.push(`typedef struct ${fullName} {`);
561
+ const { fullName } = getScopedName(node, scopeName);
562
+ const lines: string[] = [`typedef struct ${fullName} {`];
467
563
 
468
564
  // Process struct members
469
565
  for (const member of node.structMember()) {
470
- const fieldName = member.IDENTIFIER().getText();
471
- const fieldType = orchestrator.generateType(member.type());
472
-
473
- // Handle array dimensions if present
474
- const arrayDims = member.arrayDimension();
475
- let dimStr = "";
476
- if (arrayDims.length > 0) {
477
- dimStr = orchestrator.generateArrayDimensions(arrayDims);
478
- }
479
-
480
- // Handle string capacity for string fields
481
- if (member.type().stringType()) {
482
- const stringCtx = member.type().stringType()!;
483
- const intLiteral = stringCtx.INTEGER_LITERAL();
484
- if (intLiteral) {
485
- const capacity = Number.parseInt(intLiteral.getText(), 10);
486
- dimStr += `[${capacity + 1}]`;
487
- }
488
- }
489
-
490
- lines.push(` ${fieldType} ${fieldName}${dimStr};`);
566
+ lines.push(generateScopedStructField(member, orchestrator));
491
567
  }
492
568
 
493
569
  lines.push(`} ${fullName};`, "");