c-next 0.1.62 → 0.1.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -63
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +3 -2
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
|
@@ -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(
|
|
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: () =>
|
|
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(
|
|
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 },
|
|
@@ -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
|
|
|
@@ -7,18 +7,12 @@
|
|
|
7
7
|
* - Array initializers with size inference: u8 data[] <- [1, 2, 3]
|
|
8
8
|
* - Fill-all syntax: u8 data[10] <- [0*]
|
|
9
9
|
* - Array size validation
|
|
10
|
+
*
|
|
11
|
+
* Migrated to use CodeGenState instead of constructor DI.
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Array initialization tracking state.
|
|
17
|
-
*/
|
|
18
|
-
interface IArrayInitState {
|
|
19
|
-
lastArrayInitCount: number;
|
|
20
|
-
lastArrayFillValue: string | undefined;
|
|
21
|
-
}
|
|
15
|
+
import CodeGenState from "../CodeGenState.js";
|
|
22
16
|
|
|
23
17
|
/**
|
|
24
18
|
* Result from processing array initialization.
|
|
@@ -33,18 +27,10 @@ interface IArrayInitResult {
|
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
/**
|
|
36
|
-
*
|
|
30
|
+
* Callbacks required for array initialization.
|
|
31
|
+
* These need CodeGenerator context and cannot be replaced with static state.
|
|
37
32
|
*/
|
|
38
|
-
interface
|
|
39
|
-
/** Type registry for updating inferred dimensions */
|
|
40
|
-
typeRegistry: Map<string, TTypeInfo>;
|
|
41
|
-
/** Local arrays set for tracking */
|
|
42
|
-
localArrays: Set<string>;
|
|
43
|
-
/** Array initialization tracking state */
|
|
44
|
-
arrayInitState: IArrayInitState;
|
|
45
|
-
/** Get/set expected type context */
|
|
46
|
-
getExpectedType: () => string | null;
|
|
47
|
-
setExpectedType: (type: string | null) => void;
|
|
33
|
+
interface IArrayInitCallbacks {
|
|
48
34
|
/** Generate expression code */
|
|
49
35
|
generateExpression: (ctx: Parser.ExpressionContext) => string;
|
|
50
36
|
/** Get type name from type context */
|
|
@@ -57,12 +43,6 @@ interface IArrayInitHelperDeps {
|
|
|
57
43
|
* Handles array initialization with size inference and fill-all syntax.
|
|
58
44
|
*/
|
|
59
45
|
class ArrayInitHelper {
|
|
60
|
-
private readonly deps: IArrayInitHelperDeps;
|
|
61
|
-
|
|
62
|
-
constructor(deps: IArrayInitHelperDeps) {
|
|
63
|
-
this.deps = deps;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
46
|
/**
|
|
67
47
|
* Process array initialization expression.
|
|
68
48
|
* Returns null if not an array initializer pattern.
|
|
@@ -73,33 +53,46 @@ class ArrayInitHelper {
|
|
|
73
53
|
* @param arrayDims - Array dimension contexts
|
|
74
54
|
* @param hasEmptyArrayDim - Whether any dimension is empty (for inference)
|
|
75
55
|
* @param declaredSize - First dimension size if explicit, null otherwise
|
|
56
|
+
* @param callbacks - Callbacks to CodeGenerator methods
|
|
76
57
|
*/
|
|
77
|
-
processArrayInit(
|
|
58
|
+
static processArrayInit(
|
|
78
59
|
name: string,
|
|
79
60
|
typeCtx: Parser.TypeContext,
|
|
80
61
|
expression: Parser.ExpressionContext,
|
|
81
62
|
arrayDims: Parser.ArrayDimensionContext[],
|
|
82
63
|
hasEmptyArrayDim: boolean,
|
|
83
64
|
declaredSize: number | null,
|
|
65
|
+
callbacks: IArrayInitCallbacks,
|
|
84
66
|
): IArrayInitResult | null {
|
|
85
67
|
// Reset and generate initializer
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
CodeGenState.lastArrayInitCount = 0;
|
|
69
|
+
CodeGenState.lastArrayFillValue = undefined;
|
|
88
70
|
|
|
89
|
-
const initValue =
|
|
71
|
+
const initValue = ArrayInitHelper._generateArrayInitValue(
|
|
72
|
+
typeCtx,
|
|
73
|
+
expression,
|
|
74
|
+
callbacks,
|
|
75
|
+
);
|
|
90
76
|
|
|
91
77
|
// Check if it was an array initializer
|
|
92
|
-
if (!
|
|
78
|
+
if (!ArrayInitHelper._isArrayInitializer()) {
|
|
93
79
|
return null;
|
|
94
80
|
}
|
|
95
81
|
|
|
96
|
-
|
|
82
|
+
CodeGenState.localArrays.add(name);
|
|
97
83
|
|
|
98
84
|
const dimensionSuffix = hasEmptyArrayDim
|
|
99
|
-
?
|
|
100
|
-
:
|
|
101
|
-
|
|
102
|
-
|
|
85
|
+
? ArrayInitHelper._processSizeInference(name)
|
|
86
|
+
: ArrayInitHelper._processExplicitSize(
|
|
87
|
+
arrayDims,
|
|
88
|
+
declaredSize,
|
|
89
|
+
callbacks,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const finalInitValue = ArrayInitHelper._expandFillAllSyntax(
|
|
93
|
+
initValue,
|
|
94
|
+
declaredSize,
|
|
95
|
+
);
|
|
103
96
|
|
|
104
97
|
return { isArrayInit: true, dimensionSuffix, initValue: finalInitValue };
|
|
105
98
|
}
|
|
@@ -107,66 +100,66 @@ class ArrayInitHelper {
|
|
|
107
100
|
/**
|
|
108
101
|
* Generate the array initializer value with proper expected type
|
|
109
102
|
*/
|
|
110
|
-
private _generateArrayInitValue(
|
|
103
|
+
private static _generateArrayInitValue(
|
|
111
104
|
typeCtx: Parser.TypeContext,
|
|
112
105
|
expression: Parser.ExpressionContext,
|
|
106
|
+
callbacks: IArrayInitCallbacks,
|
|
113
107
|
): string {
|
|
114
|
-
const typeName =
|
|
115
|
-
const savedExpectedType =
|
|
116
|
-
|
|
117
|
-
const initValue =
|
|
118
|
-
|
|
108
|
+
const typeName = callbacks.getTypeName(typeCtx);
|
|
109
|
+
const savedExpectedType = CodeGenState.expectedType;
|
|
110
|
+
CodeGenState.expectedType = typeName;
|
|
111
|
+
const initValue = callbacks.generateExpression(expression);
|
|
112
|
+
CodeGenState.expectedType = savedExpectedType;
|
|
119
113
|
return initValue;
|
|
120
114
|
}
|
|
121
115
|
|
|
122
116
|
/**
|
|
123
117
|
* Check if the last expression was an array initializer
|
|
124
118
|
*/
|
|
125
|
-
private _isArrayInitializer(): boolean {
|
|
119
|
+
private static _isArrayInitializer(): boolean {
|
|
126
120
|
return (
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
CodeGenState.lastArrayInitCount > 0 ||
|
|
122
|
+
CodeGenState.lastArrayFillValue !== undefined
|
|
129
123
|
);
|
|
130
124
|
}
|
|
131
125
|
|
|
132
126
|
/**
|
|
133
127
|
* Process size inference for empty array dimension (u8 data[] <- [1, 2, 3])
|
|
134
128
|
*/
|
|
135
|
-
private _processSizeInference(name: string): string {
|
|
136
|
-
if (
|
|
129
|
+
private static _processSizeInference(name: string): string {
|
|
130
|
+
if (CodeGenState.lastArrayFillValue !== undefined) {
|
|
137
131
|
throw new Error(
|
|
138
|
-
`Error: Fill-all syntax [${
|
|
132
|
+
`Error: Fill-all syntax [${CodeGenState.lastArrayFillValue}*] requires explicit array size`,
|
|
139
133
|
);
|
|
140
134
|
}
|
|
141
135
|
|
|
142
136
|
// Update type registry with inferred size for .length support
|
|
143
|
-
const existingType =
|
|
137
|
+
const existingType = CodeGenState.typeRegistry.get(name);
|
|
144
138
|
if (existingType) {
|
|
145
|
-
existingType.arrayDimensions = [
|
|
146
|
-
this.deps.arrayInitState.lastArrayInitCount,
|
|
147
|
-
];
|
|
139
|
+
existingType.arrayDimensions = [CodeGenState.lastArrayInitCount];
|
|
148
140
|
}
|
|
149
141
|
|
|
150
|
-
return `[${
|
|
142
|
+
return `[${CodeGenState.lastArrayInitCount}]`;
|
|
151
143
|
}
|
|
152
144
|
|
|
153
145
|
/**
|
|
154
146
|
* Process explicit array size with validation
|
|
155
147
|
*/
|
|
156
|
-
private _processExplicitSize(
|
|
148
|
+
private static _processExplicitSize(
|
|
157
149
|
arrayDims: Parser.ArrayDimensionContext[],
|
|
158
150
|
declaredSize: number | null,
|
|
151
|
+
callbacks: IArrayInitCallbacks,
|
|
159
152
|
): string {
|
|
160
|
-
const dimensionSuffix =
|
|
153
|
+
const dimensionSuffix = callbacks.generateArrayDimensions(arrayDims);
|
|
161
154
|
|
|
162
155
|
// Validate size matches if not using fill-all
|
|
163
156
|
if (
|
|
164
157
|
declaredSize !== null &&
|
|
165
|
-
|
|
166
|
-
|
|
158
|
+
CodeGenState.lastArrayFillValue === undefined &&
|
|
159
|
+
CodeGenState.lastArrayInitCount !== declaredSize
|
|
167
160
|
) {
|
|
168
161
|
throw new Error(
|
|
169
|
-
`Error: Array size mismatch - declared [${declaredSize}] but got ${
|
|
162
|
+
`Error: Array size mismatch - declared [${declaredSize}] but got ${CodeGenState.lastArrayInitCount} elements`,
|
|
170
163
|
);
|
|
171
164
|
}
|
|
172
165
|
|
|
@@ -176,18 +169,18 @@ class ArrayInitHelper {
|
|
|
176
169
|
/**
|
|
177
170
|
* Expand fill-all syntax (e.g., [0*] with size 5 -> {0, 0, 0, 0, 0})
|
|
178
171
|
*/
|
|
179
|
-
private _expandFillAllSyntax(
|
|
172
|
+
private static _expandFillAllSyntax(
|
|
180
173
|
initValue: string,
|
|
181
174
|
declaredSize: number | null,
|
|
182
175
|
): string {
|
|
183
176
|
if (
|
|
184
|
-
|
|
177
|
+
CodeGenState.lastArrayFillValue === undefined ||
|
|
185
178
|
declaredSize === null
|
|
186
179
|
) {
|
|
187
180
|
return initValue;
|
|
188
181
|
}
|
|
189
182
|
|
|
190
|
-
const fillVal =
|
|
183
|
+
const fillVal = CodeGenState.lastArrayFillValue;
|
|
191
184
|
// C handles {0} correctly, no need to expand
|
|
192
185
|
if (fillVal === "0") {
|
|
193
186
|
return initValue;
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Sets up expectedType and assignmentContext for expression generation,
|
|
7
7
|
* enabling type-aware resolution of unqualified enum members and overflow behavior.
|
|
8
|
+
*
|
|
9
|
+
* Migrated to use CodeGenState instead of constructor DI.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
|
|
11
|
-
import TTypeInfo from "../types/TTypeInfo.js";
|
|
12
13
|
import TOverflowBehavior from "../types/TOverflowBehavior.js";
|
|
13
14
|
import analyzePostfixOps from "../../../../utils/PostfixAnalysisUtils.js";
|
|
15
|
+
import CodeGenState from "../CodeGenState.js";
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Result of resolving expected type for an assignment target.
|
|
@@ -31,41 +33,25 @@ interface IAssignmentContext {
|
|
|
31
33
|
overflowBehavior: TOverflowBehavior;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
/**
|
|
35
|
-
* Dependencies required for expected type resolution.
|
|
36
|
-
*/
|
|
37
|
-
interface IExpectedTypeResolverDeps {
|
|
38
|
-
/** Type registry for looking up variable types */
|
|
39
|
-
readonly typeRegistry: ReadonlyMap<string, TTypeInfo>;
|
|
40
|
-
/** Struct field types: structName -> (fieldName -> fieldType) */
|
|
41
|
-
readonly structFields: ReadonlyMap<string, ReadonlyMap<string, string>>;
|
|
42
|
-
/** Check if a type is a known struct */
|
|
43
|
-
isKnownStruct: (typeName: string) => boolean;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
36
|
/**
|
|
47
37
|
* Resolves expected type for assignment targets.
|
|
48
38
|
*/
|
|
49
39
|
class AssignmentExpectedTypeResolver {
|
|
50
|
-
private readonly deps: IExpectedTypeResolverDeps;
|
|
51
|
-
|
|
52
|
-
constructor(deps: IExpectedTypeResolverDeps) {
|
|
53
|
-
this.deps = deps;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
40
|
/**
|
|
57
41
|
* Resolve expected type for an assignment target.
|
|
58
42
|
*
|
|
59
43
|
* @param targetCtx - The assignment target context
|
|
60
44
|
* @returns The resolved expected type and assignment context
|
|
61
45
|
*/
|
|
62
|
-
resolve(
|
|
46
|
+
static resolve(
|
|
47
|
+
targetCtx: Parser.AssignmentTargetContext,
|
|
48
|
+
): IExpectedTypeResult {
|
|
63
49
|
const postfixOps = targetCtx.postfixTargetOp();
|
|
64
50
|
const baseId = targetCtx.IDENTIFIER()?.getText();
|
|
65
51
|
|
|
66
52
|
// Case 1: Simple identifier (x <- value) - no postfix ops
|
|
67
53
|
if (baseId && postfixOps.length === 0) {
|
|
68
|
-
return
|
|
54
|
+
return AssignmentExpectedTypeResolver.resolveForSimpleIdentifier(baseId);
|
|
69
55
|
}
|
|
70
56
|
|
|
71
57
|
// Case 2: Has member access - extract identifiers from postfix chain
|
|
@@ -77,7 +63,9 @@ class AssignmentExpectedTypeResolver {
|
|
|
77
63
|
|
|
78
64
|
// If we have member access (multiple identifiers), resolve for member chain
|
|
79
65
|
if (identifiers.length >= 2 && !hasSubscript) {
|
|
80
|
-
return
|
|
66
|
+
return AssignmentExpectedTypeResolver.resolveForMemberChain(
|
|
67
|
+
identifiers,
|
|
68
|
+
);
|
|
81
69
|
}
|
|
82
70
|
}
|
|
83
71
|
|
|
@@ -88,8 +76,8 @@ class AssignmentExpectedTypeResolver {
|
|
|
88
76
|
/**
|
|
89
77
|
* Resolve expected type for a simple identifier target.
|
|
90
78
|
*/
|
|
91
|
-
private resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
|
|
92
|
-
const typeInfo =
|
|
79
|
+
private static resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
|
|
80
|
+
const typeInfo = CodeGenState.typeRegistry.get(id);
|
|
93
81
|
if (!typeInfo) {
|
|
94
82
|
return { expectedType: null, assignmentContext: null };
|
|
95
83
|
}
|
|
@@ -111,15 +99,17 @@ class AssignmentExpectedTypeResolver {
|
|
|
111
99
|
* Issue #452: Enables type-aware resolution of unqualified enum members
|
|
112
100
|
* for nested access (e.g., config.nested.field).
|
|
113
101
|
*/
|
|
114
|
-
private resolveForMemberChain(
|
|
102
|
+
private static resolveForMemberChain(
|
|
103
|
+
identifiers: string[],
|
|
104
|
+
): IExpectedTypeResult {
|
|
115
105
|
if (identifiers.length < 2) {
|
|
116
106
|
return { expectedType: null, assignmentContext: null };
|
|
117
107
|
}
|
|
118
108
|
|
|
119
109
|
const rootName = identifiers[0];
|
|
120
|
-
const rootTypeInfo =
|
|
110
|
+
const rootTypeInfo = CodeGenState.typeRegistry.get(rootName);
|
|
121
111
|
|
|
122
|
-
if (!rootTypeInfo || !
|
|
112
|
+
if (!rootTypeInfo || !CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
|
|
123
113
|
return { expectedType: null, assignmentContext: null };
|
|
124
114
|
}
|
|
125
115
|
|
|
@@ -128,18 +118,19 @@ class AssignmentExpectedTypeResolver {
|
|
|
128
118
|
// Walk through each member in the chain to find the final field's type
|
|
129
119
|
for (let i = 1; i < identifiers.length && currentStructType; i++) {
|
|
130
120
|
const memberName = identifiers[i];
|
|
131
|
-
const structFieldTypes =
|
|
121
|
+
const structFieldTypes: ReadonlyMap<string, string> | undefined =
|
|
122
|
+
CodeGenState.symbols?.structFields.get(currentStructType);
|
|
132
123
|
|
|
133
124
|
if (!structFieldTypes?.has(memberName)) {
|
|
134
125
|
break;
|
|
135
126
|
}
|
|
136
127
|
|
|
137
|
-
const memberType = structFieldTypes.get(memberName)!;
|
|
128
|
+
const memberType: string = structFieldTypes.get(memberName)!;
|
|
138
129
|
|
|
139
130
|
if (i === identifiers.length - 1) {
|
|
140
131
|
// Last field in chain - this is the assignment target's type
|
|
141
132
|
return { expectedType: memberType, assignmentContext: null };
|
|
142
|
-
} else if (
|
|
133
|
+
} else if (CodeGenState.isKnownStruct(memberType)) {
|
|
143
134
|
// Intermediate field - continue walking if it's a struct
|
|
144
135
|
currentStructType = memberType;
|
|
145
136
|
} else {
|