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
@@ -13,6 +13,7 @@ import IGeneratorInput from "../IGeneratorInput";
13
13
  import IGeneratorState from "../IGeneratorState";
14
14
  import IGeneratorOutput from "../IGeneratorOutput";
15
15
  import IOrchestrator from "../IOrchestrator";
16
+ import generateRegisterMacros from "./RegisterMacroGenerator";
16
17
 
17
18
  /**
18
19
  * Generate register macros with scope prefix.
@@ -28,35 +29,25 @@ const generateScopedRegister = (
28
29
  const fullName = `${scopeName}_${name}`; // Teensy4_GPIO7
29
30
  const baseAddress = orchestrator.generateExpression(node.expression());
30
31
 
31
- const lines: string[] = [];
32
- lines.push(`/* Register: ${fullName} @ ${baseAddress} */`);
33
-
34
- // Generate individual #define for each register member with its offset
35
- for (const member of node.registerMember()) {
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
- // Check if the type is a scoped bitmap (e.g., GPIO7Pins -> Teensy4_GPIO7Pins)
32
+ // Type resolver for scoped bitmaps (e.g., GPIO7Pins -> Teensy4_GPIO7Pins)
33
+ const resolveType = (regType: string): string | undefined => {
42
34
  const scopedTypeName = `${scopeName}_${regType}`;
43
- if (input.symbols?.knownBitmaps.has(scopedTypeName)) {
44
- regType = scopedTypeName;
45
- }
46
-
47
- // Determine qualifiers based on access mode
48
- let cast = `volatile ${regType}*`;
49
- if (access === "ro") {
50
- cast = `volatile ${regType} const *`;
51
- }
52
-
53
- // Generate: #define Teensy4_GPIO7_DR (*(volatile uint32_t*)(0x42004000 + 0x00))
54
- lines.push(
55
- `#define ${fullName}_${regName} (*(${cast})(${baseAddress} + ${offset}))`,
56
- );
57
- }
35
+ return input.symbols?.knownBitmaps.has(scopedTypeName)
36
+ ? scopedTypeName
37
+ : undefined;
38
+ };
58
39
 
59
- lines.push("");
40
+ const lines: string[] = [
41
+ `/* Register: ${fullName} @ ${baseAddress} */`,
42
+ ...generateRegisterMacros(
43
+ node.registerMember(),
44
+ fullName,
45
+ baseAddress,
46
+ orchestrator,
47
+ resolveType,
48
+ ),
49
+ "",
50
+ ];
60
51
 
61
52
  return {
62
53
  code: lines.join("\n"),
@@ -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
+ });
@@ -42,10 +42,35 @@ function createMockConstModifier(
42
42
  return isConst ? ({} as Parser.ConstModifierContext) : null;
43
43
  }
44
44
 
45
+ /**
46
+ * Create a mock array type context.
47
+ */
48
+ function createMockArrayType(sizeExpr?: string | null) {
49
+ if (sizeExpr === null) {
50
+ // Empty brackets - no expression
51
+ return {
52
+ expression: () => null,
53
+ };
54
+ }
55
+ return {
56
+ expression: () =>
57
+ sizeExpr
58
+ ? {
59
+ getText: () => sizeExpr,
60
+ __mockValue: sizeExpr,
61
+ }
62
+ : null,
63
+ };
64
+ }
65
+
45
66
  /**
46
67
  * Create a mock type context.
47
68
  */
48
- function createMockType(typeName: string, hasStringType = false) {
69
+ function createMockType(
70
+ typeName: string,
71
+ hasStringType = false,
72
+ arrayTypeSize?: string | null,
73
+ ) {
49
74
  return {
50
75
  getText: () => typeName,
51
76
  stringType: () =>
@@ -54,6 +79,8 @@ function createMockType(typeName: string, hasStringType = false) {
54
79
  INTEGER_LITERAL: () => ({ getText: () => "32" }),
55
80
  }
56
81
  : null,
82
+ arrayType: () =>
83
+ arrayTypeSize !== undefined ? createMockArrayType(arrayTypeSize) : null,
57
84
  };
58
85
  }
59
86
 
@@ -99,10 +126,16 @@ function createMockVariableDecl(options: {
99
126
  hasStringType?: boolean;
100
127
  constructorArgs?: string[];
101
128
  startLine?: number;
129
+ arrayTypeSize?: string | null; // C-Next style: u16[8] name
102
130
  }) {
103
131
  return {
104
132
  IDENTIFIER: () => ({ getText: () => options.name }),
105
- type: () => createMockType(options.type, options.hasStringType),
133
+ type: () =>
134
+ createMockType(
135
+ options.type,
136
+ options.hasStringType,
137
+ options.arrayTypeSize,
138
+ ),
106
139
  constModifier: () => createMockConstModifier(options.isConst ?? false),
107
140
  expression: () =>
108
141
  options.initialValue ? createMockExpression(options.initialValue) : null,
@@ -207,10 +240,11 @@ function createMockStructMember(
207
240
  type: string,
208
241
  arrayDims?: string[],
209
242
  hasStringType = false,
243
+ arrayTypeSize?: string | null,
210
244
  ) {
211
245
  return {
212
246
  IDENTIFIER: () => ({ getText: () => name }),
213
- type: () => createMockType(type, hasStringType),
247
+ type: () => createMockType(type, hasStringType, arrayTypeSize),
214
248
  arrayDimension: () => (arrayDims ?? []).map(createMockArrayDimension),
215
249
  };
216
250
  }
@@ -225,13 +259,20 @@ function createMockStructDecl(
225
259
  type: string;
226
260
  arrayDims?: string[];
227
261
  hasStringType?: boolean;
262
+ arrayTypeSize?: string | null;
228
263
  }>,
229
264
  ) {
230
265
  return {
231
266
  IDENTIFIER: () => ({ getText: () => name }),
232
267
  structMember: () =>
233
268
  members.map((m) =>
234
- createMockStructMember(m.name, m.type, m.arrayDims, m.hasStringType),
269
+ createMockStructMember(
270
+ m.name,
271
+ m.type,
272
+ m.arrayDims,
273
+ m.hasStringType,
274
+ m.arrayTypeSize,
275
+ ),
235
276
  ),
236
277
  };
237
278
  }
@@ -579,6 +620,66 @@ describe("ScopeGenerator", () => {
579
620
  expect(result.code).toContain("static uint8_t Serial_buffer[256] = {0};");
580
621
  });
581
622
 
623
+ it("generates C-Next style array variable with constant size", () => {
624
+ const varDecl = createMockVariableDecl({
625
+ name: "data",
626
+ type: "u16[8]",
627
+ arrayTypeSize: "8",
628
+ });
629
+ const member = createMockScopeMember({ variableDecl: varDecl });
630
+ const ctx = createMockScopeContext("Buffer", [member]);
631
+ const input = createMockInput();
632
+ const state = createMockState();
633
+ const orchestrator = createMockOrchestrator({
634
+ ...createMockOrchestrator(),
635
+ tryEvaluateConstant: vi.fn(() => 8),
636
+ });
637
+
638
+ const result = generateScope(ctx, input, state, orchestrator);
639
+
640
+ expect(result.code).toContain("static u16[8] Buffer_data[8] = {0};");
641
+ });
642
+
643
+ it("generates C-Next style array variable with non-constant expression (fallback)", () => {
644
+ const varDecl = createMockVariableDecl({
645
+ name: "items",
646
+ type: "u16[BUFFER_SIZE]",
647
+ arrayTypeSize: "BUFFER_SIZE",
648
+ });
649
+ const member = createMockScopeMember({ variableDecl: varDecl });
650
+ const ctx = createMockScopeContext("Storage", [member]);
651
+ const input = createMockInput();
652
+ const state = createMockState();
653
+ const orchestrator = createMockOrchestrator({
654
+ ...createMockOrchestrator(),
655
+ tryEvaluateConstant: vi.fn(() => undefined), // Can't resolve macro
656
+ generateExpression: vi.fn(() => "BUFFER_SIZE"),
657
+ });
658
+
659
+ const result = generateScope(ctx, input, state, orchestrator);
660
+
661
+ expect(result.code).toContain(
662
+ "static u16[BUFFER_SIZE] Storage_items[BUFFER_SIZE] = {0};",
663
+ );
664
+ });
665
+
666
+ it("generates C-Next style array variable with no size (empty brackets)", () => {
667
+ const varDecl = createMockVariableDecl({
668
+ name: "flexible",
669
+ type: "u8[]",
670
+ arrayTypeSize: null, // No size expression
671
+ });
672
+ const member = createMockScopeMember({ variableDecl: varDecl });
673
+ const ctx = createMockScopeContext("Dynamic", [member]);
674
+ const input = createMockInput();
675
+ const state = createMockState();
676
+ const orchestrator = createMockOrchestrator();
677
+
678
+ const result = generateScope(ctx, input, state, orchestrator);
679
+
680
+ expect(result.code).toContain("static u8[] Dynamic_flexible[] = {0};");
681
+ });
682
+
582
683
  it("generates string variable with capacity dimension (ADR-045)", () => {
583
684
  const varDecl = createMockVariableDecl({
584
685
  name: "message",
@@ -1036,6 +1137,58 @@ describe("ScopeGenerator", () => {
1036
1137
  expect(result.code).toContain("uint8_t data[256];");
1037
1138
  });
1038
1139
 
1140
+ it("generates struct field with C-Next array type constant size", () => {
1141
+ const structDecl = createMockStructDecl("Container", [
1142
+ { name: "items", type: "u16[4]", arrayTypeSize: "4" },
1143
+ ]);
1144
+ const member = createMockScopeMember({ structDecl: structDecl });
1145
+ const ctx = createMockScopeContext("Data", [member]);
1146
+ const input = createMockInput();
1147
+ const state = createMockState();
1148
+ const orchestrator = createMockOrchestrator({
1149
+ ...createMockOrchestrator(),
1150
+ tryEvaluateConstant: vi.fn(() => 4),
1151
+ });
1152
+
1153
+ const result = generateScope(ctx, input, state, orchestrator);
1154
+
1155
+ expect(result.code).toContain("u16[4] items[4];");
1156
+ });
1157
+
1158
+ it("generates struct field with C-Next array type non-constant (fallback)", () => {
1159
+ const structDecl = createMockStructDecl("FlexContainer", [
1160
+ { name: "buffer", type: "u8[MAX_SIZE]", arrayTypeSize: "MAX_SIZE" },
1161
+ ]);
1162
+ const member = createMockScopeMember({ structDecl: structDecl });
1163
+ const ctx = createMockScopeContext("Flex", [member]);
1164
+ const input = createMockInput();
1165
+ const state = createMockState();
1166
+ const orchestrator = createMockOrchestrator({
1167
+ ...createMockOrchestrator(),
1168
+ tryEvaluateConstant: vi.fn(() => undefined), // Can't resolve macro
1169
+ generateExpression: vi.fn(() => "MAX_SIZE"),
1170
+ });
1171
+
1172
+ const result = generateScope(ctx, input, state, orchestrator);
1173
+
1174
+ expect(result.code).toContain("u8[MAX_SIZE] buffer[MAX_SIZE];");
1175
+ });
1176
+
1177
+ it("generates struct field with C-Next array type no size (empty brackets)", () => {
1178
+ const structDecl = createMockStructDecl("DynamicContainer", [
1179
+ { name: "data", type: "u8[]", arrayTypeSize: null },
1180
+ ]);
1181
+ const member = createMockScopeMember({ structDecl: structDecl });
1182
+ const ctx = createMockScopeContext("Dyn", [member]);
1183
+ const input = createMockInput();
1184
+ const state = createMockState();
1185
+ const orchestrator = createMockOrchestrator();
1186
+
1187
+ const result = generateScope(ctx, input, state, orchestrator);
1188
+
1189
+ expect(result.code).toContain("u8[] data[];");
1190
+ });
1191
+
1039
1192
  it("generates struct field with string capacity", () => {
1040
1193
  const structDecl = createMockStructDecl("Message", [
1041
1194
  { name: "text", type: "string<64>", hasStringType: true },
@@ -931,7 +931,11 @@ const tryKnownScopeAccess = (
931
931
  state.currentScope,
932
932
  );
933
933
  }
934
- orchestrator.validateCrossScopeVisibility(ctx.result, ctx.memberName);
934
+ orchestrator.validateCrossScopeVisibility(
935
+ ctx.result,
936
+ ctx.memberName,
937
+ ctx.isGlobalAccess,
938
+ );
935
939
 
936
940
  const output = initializeMemberOutput(ctx);
937
941
  output.result = `${ctx.result}${orchestrator.getScopeSeparator(ctx.isCppAccessChain)}${ctx.memberName}`;
@@ -25,23 +25,7 @@ import IGeneratorState from "../IGeneratorState";
25
25
  import IOrchestrator from "../IOrchestrator";
26
26
  import VariableModifierBuilder from "../../helpers/VariableModifierBuilder";
27
27
  import ExpressionUtils from "../../../../../utils/ExpressionUtils";
28
-
29
- /**
30
- * Maps C-Next assignment operators to C assignment operators
31
- */
32
- const ASSIGNMENT_OPERATOR_MAP: Record<string, string> = {
33
- "<-": "=",
34
- "+<-": "+=",
35
- "-<-": "-=",
36
- "*<-": "*=",
37
- "/<-": "/=",
38
- "%<-": "%=",
39
- "&<-": "&=",
40
- "|<-": "|=",
41
- "^<-": "^=",
42
- "<<<-": "<<=",
43
- ">><-": ">>=",
44
- };
28
+ import ASSIGNMENT_OPERATOR_MAP from "../../../../../utils/constants/OperatorMappings";
45
29
 
46
30
  /**
47
31
  * Issue #477: Check if a simple identifier is an unqualified enum member.
@@ -11,6 +11,27 @@
11
11
  import TYPE_MAP from "../../types/TYPE_MAP";
12
12
  import OverflowHelperTemplates from "./OverflowHelperTemplates";
13
13
 
14
+ /**
15
+ * Generate a safe arithmetic helper function (div or mod).
16
+ * Extracted to eliminate duplication between safe_div and safe_mod generation.
17
+ */
18
+ const generateSafeArithmeticHelper = (
19
+ opName: string,
20
+ opSymbol: string,
21
+ cnxType: string,
22
+ cType: string,
23
+ ): string[] => [
24
+ `static inline bool cnx_safe_${opName}_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
25
+ ` if (divisor == 0) {`,
26
+ ` *output = defaultValue;`,
27
+ ` return true; // Error occurred`,
28
+ ` }`,
29
+ ` *output = numerator ${opSymbol} divisor;`,
30
+ ` return false; // Success`,
31
+ `}`,
32
+ "",
33
+ ];
34
+
14
35
  /**
15
36
  * Generate all needed overflow helper functions
16
37
  * ADR-044: Overflow helper functions with clamping or panic behavior
@@ -90,32 +111,12 @@ const generateSafeDivHelpers = (
90
111
 
91
112
  // Generate safe_div helper if needed
92
113
  if (needsDiv) {
93
- lines.push(
94
- `static inline bool cnx_safe_div_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
95
- ` if (divisor == 0) {`,
96
- ` *output = defaultValue;`,
97
- ` return true; // Error occurred`,
98
- ` }`,
99
- ` *output = numerator / divisor;`,
100
- ` return false; // Success`,
101
- `}`,
102
- "",
103
- );
114
+ lines.push(...generateSafeArithmeticHelper("div", "/", cnxType, cType));
104
115
  }
105
116
 
106
117
  // Generate safe_mod helper if needed
107
118
  if (needsMod) {
108
- lines.push(
109
- `static inline bool cnx_safe_mod_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
110
- ` if (divisor == 0) {`,
111
- ` *output = defaultValue;`,
112
- ` return true; // Error occurred`,
113
- ` }`,
114
- ` *output = numerator % divisor;`,
115
- ` return false; // Success`,
116
- `}`,
117
- "",
118
- );
119
+ lines.push(...generateSafeArithmeticHelper("mod", "%", cnxType, cType));
119
120
  }
120
121
  }
121
122