c-next 0.1.68 → 0.1.70
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/package.json +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +240 -204
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +693 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +86 -5
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/AssignmentTargetExtractor.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/ChildStatementCollector.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/StatementExpressionCollector.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/AssignmentTargetExtractor.test.ts +2 -2
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/ChildStatementCollector.test.ts +2 -2
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/StatementExpressionCollector.test.ts +2 -2
- package/src/transpiler/output/codegen/CodeGenerator.ts +160 -742
- package/src/transpiler/output/codegen/TypeRegistrationUtils.ts +4 -6
- package/src/transpiler/output/codegen/TypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/TypeValidator.ts +7 -7
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +29 -1
- package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +4 -6
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +4 -2
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +49 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +15 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +30 -17
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +25 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +19 -8
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +9 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +41 -26
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +29 -37
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +27 -19
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +10 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +51 -33
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +9 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +6 -1
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
- package/src/transpiler/state/CodeGenState.ts +157 -5
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +274 -6
- /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
- /package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/TransitiveModificationPropagator.test.ts +0 -0
|
@@ -139,7 +139,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
139
139
|
|
|
140
140
|
it("returns isBitAccess false when last op is member access", () => {
|
|
141
141
|
// point.flags (no subscript at end)
|
|
142
|
-
CodeGenState.
|
|
142
|
+
CodeGenState.setVariableTypeInfo("point", {
|
|
143
143
|
baseType: "Point",
|
|
144
144
|
bitWidth: 0,
|
|
145
145
|
isArray: false,
|
|
@@ -159,7 +159,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
159
159
|
|
|
160
160
|
it("returns isBitAccess false when last subscript has 2 expressions (bit range)", () => {
|
|
161
161
|
// flags[0, 8] - bit range, not single bit access
|
|
162
|
-
CodeGenState.
|
|
162
|
+
CodeGenState.setVariableTypeInfo("flags", {
|
|
163
163
|
baseType: "u32",
|
|
164
164
|
bitWidth: 32,
|
|
165
165
|
isArray: false,
|
|
@@ -180,7 +180,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
180
180
|
pointFields.set("flags", "u8");
|
|
181
181
|
setupStructFields("Point", pointFields);
|
|
182
182
|
|
|
183
|
-
CodeGenState.
|
|
183
|
+
CodeGenState.setVariableTypeInfo("point", {
|
|
184
184
|
baseType: "Point",
|
|
185
185
|
bitWidth: 0,
|
|
186
186
|
isArray: false,
|
|
@@ -209,7 +209,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
209
209
|
gridFields.set("items", "u8");
|
|
210
210
|
setupStructFields("Grid", gridFields, new Set(["items"]));
|
|
211
211
|
|
|
212
|
-
CodeGenState.
|
|
212
|
+
CodeGenState.setVariableTypeInfo("grid", {
|
|
213
213
|
baseType: "Grid",
|
|
214
214
|
bitWidth: 0,
|
|
215
215
|
isArray: false,
|
|
@@ -236,7 +236,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
236
236
|
pointFields.set("name", "string");
|
|
237
237
|
setupStructFields("Point", pointFields);
|
|
238
238
|
|
|
239
|
-
CodeGenState.
|
|
239
|
+
CodeGenState.setVariableTypeInfo("point", {
|
|
240
240
|
baseType: "Point",
|
|
241
241
|
bitWidth: 0,
|
|
242
242
|
isArray: false,
|
|
@@ -263,7 +263,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
263
263
|
deviceFields.set("flags", "u8");
|
|
264
264
|
setupStructFields("Device", deviceFields);
|
|
265
265
|
|
|
266
|
-
CodeGenState.
|
|
266
|
+
CodeGenState.setVariableTypeInfo("devices", {
|
|
267
267
|
baseType: "Device",
|
|
268
268
|
bitWidth: 0,
|
|
269
269
|
isArray: true,
|
|
@@ -290,7 +290,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
290
290
|
|
|
291
291
|
it("returns false for 2D array element: matrix[0][1]", () => {
|
|
292
292
|
// matrix[0][1] is array access, not bit access
|
|
293
|
-
CodeGenState.
|
|
293
|
+
CodeGenState.setVariableTypeInfo("matrix", {
|
|
294
294
|
baseType: "u8",
|
|
295
295
|
bitWidth: 8,
|
|
296
296
|
isArray: true,
|
|
@@ -315,7 +315,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
315
315
|
it("detects bit access on 2D array element: matrix[0][1][3]", () => {
|
|
316
316
|
// matrix[0][1][3] where matrix is u8[4][4]
|
|
317
317
|
// The third subscript [3] is bit access on the u8 element
|
|
318
|
-
CodeGenState.
|
|
318
|
+
CodeGenState.setVariableTypeInfo("matrix", {
|
|
319
319
|
baseType: "u8",
|
|
320
320
|
bitWidth: 8,
|
|
321
321
|
isArray: true,
|
|
@@ -357,7 +357,7 @@ describe("MemberChainAnalyzer", () => {
|
|
|
357
357
|
|
|
358
358
|
it("returns false for member access on non-struct", () => {
|
|
359
359
|
// x.field[0] where x is a primitive
|
|
360
|
-
CodeGenState.
|
|
360
|
+
CodeGenState.setVariableTypeInfo("x", {
|
|
361
361
|
baseType: "u32",
|
|
362
362
|
bitWidth: 32,
|
|
363
363
|
isArray: false,
|
|
@@ -38,7 +38,7 @@ describe("StringLengthCounter", () => {
|
|
|
38
38
|
|
|
39
39
|
describe("countExpression", () => {
|
|
40
40
|
it("counts single .length access on string variable", () => {
|
|
41
|
-
CodeGenState.
|
|
41
|
+
CodeGenState.setVariableTypeInfo("myStr", {
|
|
42
42
|
baseType: "char",
|
|
43
43
|
bitWidth: 8,
|
|
44
44
|
isArray: false,
|
|
@@ -54,7 +54,7 @@ describe("StringLengthCounter", () => {
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it("counts multiple .length accesses on same variable", () => {
|
|
57
|
-
CodeGenState.
|
|
57
|
+
CodeGenState.setVariableTypeInfo("str", {
|
|
58
58
|
baseType: "char",
|
|
59
59
|
bitWidth: 8,
|
|
60
60
|
isArray: false,
|
|
@@ -71,7 +71,7 @@ describe("StringLengthCounter", () => {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it("counts .length accesses on different string variables", () => {
|
|
74
|
-
CodeGenState.
|
|
74
|
+
CodeGenState.setVariableTypeInfo("a", {
|
|
75
75
|
baseType: "char",
|
|
76
76
|
bitWidth: 8,
|
|
77
77
|
isArray: false,
|
|
@@ -79,7 +79,7 @@ describe("StringLengthCounter", () => {
|
|
|
79
79
|
isString: true,
|
|
80
80
|
stringCapacity: 16,
|
|
81
81
|
});
|
|
82
|
-
CodeGenState.
|
|
82
|
+
CodeGenState.setVariableTypeInfo("b", {
|
|
83
83
|
baseType: "char",
|
|
84
84
|
bitWidth: 8,
|
|
85
85
|
isArray: false,
|
|
@@ -96,7 +96,7 @@ describe("StringLengthCounter", () => {
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
it("ignores .length on non-string variables", () => {
|
|
99
|
-
CodeGenState.
|
|
99
|
+
CodeGenState.setVariableTypeInfo("arr", {
|
|
100
100
|
baseType: "u8",
|
|
101
101
|
bitWidth: 8,
|
|
102
102
|
isArray: true,
|
|
@@ -111,7 +111,7 @@ describe("StringLengthCounter", () => {
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
it("ignores other member accesses", () => {
|
|
114
|
-
CodeGenState.
|
|
114
|
+
CodeGenState.setVariableTypeInfo("obj", {
|
|
115
115
|
baseType: "MyStruct",
|
|
116
116
|
bitWidth: 32,
|
|
117
117
|
isArray: false,
|
|
@@ -135,7 +135,7 @@ describe("StringLengthCounter", () => {
|
|
|
135
135
|
|
|
136
136
|
describe("countBlock", () => {
|
|
137
137
|
it("counts .length accesses across multiple statements", () => {
|
|
138
|
-
CodeGenState.
|
|
138
|
+
CodeGenState.setVariableTypeInfo("msg", {
|
|
139
139
|
baseType: "char",
|
|
140
140
|
bitWidth: 8,
|
|
141
141
|
isArray: false,
|
|
@@ -154,7 +154,7 @@ describe("StringLengthCounter", () => {
|
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
it("counts .length in assignment statements", () => {
|
|
157
|
-
CodeGenState.
|
|
157
|
+
CodeGenState.setVariableTypeInfo("text", {
|
|
158
158
|
baseType: "char",
|
|
159
159
|
bitWidth: 8,
|
|
160
160
|
isArray: false,
|
|
@@ -175,7 +175,7 @@ describe("StringLengthCounter", () => {
|
|
|
175
175
|
|
|
176
176
|
describe("countBlockInto", () => {
|
|
177
177
|
it("adds counts to existing map", () => {
|
|
178
|
-
CodeGenState.
|
|
178
|
+
CodeGenState.setVariableTypeInfo("s1", {
|
|
179
179
|
baseType: "char",
|
|
180
180
|
bitWidth: 8,
|
|
181
181
|
isArray: false,
|
|
@@ -183,7 +183,7 @@ describe("StringLengthCounter", () => {
|
|
|
183
183
|
isString: true,
|
|
184
184
|
stringCapacity: 32,
|
|
185
185
|
});
|
|
186
|
-
CodeGenState.
|
|
186
|
+
CodeGenState.setVariableTypeInfo("s2", {
|
|
187
187
|
baseType: "char",
|
|
188
188
|
bitWidth: 8,
|
|
189
189
|
isArray: false,
|
|
@@ -209,7 +209,7 @@ describe("StringLengthCounter", () => {
|
|
|
209
209
|
|
|
210
210
|
describe("nested expressions", () => {
|
|
211
211
|
it("counts .length in ternary expressions", () => {
|
|
212
|
-
CodeGenState.
|
|
212
|
+
CodeGenState.setVariableTypeInfo("str", {
|
|
213
213
|
baseType: "char",
|
|
214
214
|
bitWidth: 8,
|
|
215
215
|
isArray: false,
|
|
@@ -225,7 +225,7 @@ describe("StringLengthCounter", () => {
|
|
|
225
225
|
});
|
|
226
226
|
|
|
227
227
|
it("counts .length in comparison expressions", () => {
|
|
228
|
-
CodeGenState.
|
|
228
|
+
CodeGenState.setVariableTypeInfo("name", {
|
|
229
229
|
baseType: "char",
|
|
230
230
|
bitWidth: 8,
|
|
231
231
|
isArray: false,
|
|
@@ -144,7 +144,7 @@ class AssignmentClassifier {
|
|
|
144
144
|
varName: string,
|
|
145
145
|
fieldName: string,
|
|
146
146
|
): AssignmentKind | null {
|
|
147
|
-
const typeInfo = CodeGenState.
|
|
147
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(varName);
|
|
148
148
|
if (!typeInfo?.isBitmap || !typeInfo.bitmapTypeName) {
|
|
149
149
|
return null;
|
|
150
150
|
}
|
|
@@ -189,7 +189,7 @@ class AssignmentClassifier {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// Check if struct member bitmap field: struct.bitmapMember.field
|
|
192
|
-
const structTypeInfo = CodeGenState.
|
|
192
|
+
const structTypeInfo = CodeGenState.getVariableTypeInfo(firstName);
|
|
193
193
|
if (
|
|
194
194
|
!structTypeInfo ||
|
|
195
195
|
!CodeGenState.isKnownStruct(structTypeInfo.baseType)
|
|
@@ -270,7 +270,7 @@ class AssignmentClassifier {
|
|
|
270
270
|
|
|
271
271
|
const ids = ctx.identifiers;
|
|
272
272
|
const firstId = ids[0];
|
|
273
|
-
const typeInfo = CodeGenState.
|
|
273
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(firstId);
|
|
274
274
|
|
|
275
275
|
// Check for bit range through struct chain: devices[0].control[0, 4]
|
|
276
276
|
// Detected by last subscript having 2 expressions (start, width)
|
|
@@ -494,7 +494,7 @@ class AssignmentClassifier {
|
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
const name = ctx.identifiers[0];
|
|
497
|
-
const typeInfo = CodeGenState.
|
|
497
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(name) ?? null;
|
|
498
498
|
|
|
499
499
|
// Use shared classifier for array vs bit access decision
|
|
500
500
|
// Use lastSubscriptExprCount to distinguish [0][0] (two ops, each 1 expr)
|
|
@@ -558,16 +558,16 @@ class AssignmentClassifier {
|
|
|
558
558
|
let typeInfo;
|
|
559
559
|
if (ctx.isSimpleIdentifier) {
|
|
560
560
|
const id = ctx.identifiers[0];
|
|
561
|
-
typeInfo = CodeGenState.
|
|
561
|
+
typeInfo = CodeGenState.getVariableTypeInfo(id);
|
|
562
562
|
} else if (ctx.isSimpleThisAccess && CodeGenState.currentScope) {
|
|
563
563
|
// this.member pattern: lookup using scoped name
|
|
564
564
|
const memberName = ctx.identifiers[0];
|
|
565
565
|
const scopedName = `${CodeGenState.currentScope}_${memberName}`;
|
|
566
|
-
typeInfo = CodeGenState.
|
|
566
|
+
typeInfo = CodeGenState.getVariableTypeInfo(scopedName);
|
|
567
567
|
} else if (ctx.isSimpleGlobalAccess) {
|
|
568
568
|
// global.member pattern: lookup using direct name
|
|
569
569
|
const memberName = ctx.identifiers[0];
|
|
570
|
-
typeInfo = CodeGenState.
|
|
570
|
+
typeInfo = CodeGenState.getVariableTypeInfo(memberName);
|
|
571
571
|
} else {
|
|
572
572
|
return null;
|
|
573
573
|
}
|
|
@@ -604,7 +604,7 @@ class AssignmentClassifier {
|
|
|
604
604
|
): AssignmentKind | null {
|
|
605
605
|
if (!ctx.isSimpleIdentifier) return null;
|
|
606
606
|
const id = ctx.identifiers[0];
|
|
607
|
-
const typeInfo = CodeGenState.
|
|
607
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(id);
|
|
608
608
|
return AssignmentClassifier.isSimpleStringType(typeInfo)
|
|
609
609
|
? AssignmentKind.STRING_SIMPLE
|
|
610
610
|
: null;
|
|
@@ -619,7 +619,7 @@ class AssignmentClassifier {
|
|
|
619
619
|
if (!ctx.isSimpleThisAccess || !CodeGenState.currentScope) return null;
|
|
620
620
|
const memberName = ctx.identifiers[0];
|
|
621
621
|
const scopedName = `${CodeGenState.currentScope}_${memberName}`;
|
|
622
|
-
const typeInfo = CodeGenState.
|
|
622
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(scopedName);
|
|
623
623
|
return AssignmentClassifier.isSimpleStringType(typeInfo)
|
|
624
624
|
? AssignmentKind.STRING_THIS_MEMBER
|
|
625
625
|
: null;
|
|
@@ -633,7 +633,7 @@ class AssignmentClassifier {
|
|
|
633
633
|
): AssignmentKind | null {
|
|
634
634
|
if (!ctx.isSimpleGlobalAccess) return null;
|
|
635
635
|
const id = ctx.identifiers[0];
|
|
636
|
-
const typeInfo = CodeGenState.
|
|
636
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(id);
|
|
637
637
|
return AssignmentClassifier.isSimpleStringType(typeInfo)
|
|
638
638
|
? AssignmentKind.STRING_GLOBAL
|
|
639
639
|
: null;
|
|
@@ -644,7 +644,7 @@ class AssignmentClassifier {
|
|
|
644
644
|
* Returns the base struct type if valid, null if not a known struct.
|
|
645
645
|
*/
|
|
646
646
|
private static _resolveStructType(structName: string): string | null {
|
|
647
|
-
const structTypeInfo = CodeGenState.
|
|
647
|
+
const structTypeInfo = CodeGenState.getVariableTypeInfo(structName);
|
|
648
648
|
if (
|
|
649
649
|
!structTypeInfo ||
|
|
650
650
|
!CodeGenState.isKnownStruct(structTypeInfo.baseType)
|
|
@@ -18,6 +18,15 @@ interface IContextBuilderDeps {
|
|
|
18
18
|
|
|
19
19
|
/** Generate C expression for a value */
|
|
20
20
|
generateExpression(ctx: Parser.ExpressionContext): string;
|
|
21
|
+
|
|
22
|
+
/** Generate fully-resolved assignment target with scope prefixes */
|
|
23
|
+
generateAssignmentTarget(ctx: Parser.AssignmentTargetContext): string;
|
|
24
|
+
|
|
25
|
+
/** Check if an identifier is a known register (for skipping resolution) */
|
|
26
|
+
isKnownRegister(name: string): boolean;
|
|
27
|
+
|
|
28
|
+
/** Current scope name (for scoped register detection) */
|
|
29
|
+
currentScope: string | null;
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
/**
|
|
@@ -32,6 +41,37 @@ interface ITargetExtraction {
|
|
|
32
41
|
lastSubscriptExprCount: number;
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Extract the base identifier from a resolved target string.
|
|
46
|
+
* Removes subscripts ([...]) from the resolved target.
|
|
47
|
+
* Examples:
|
|
48
|
+
* "ArrayBug_data[0]" -> "ArrayBug_data"
|
|
49
|
+
* "matrix[i][j]" -> "matrix"
|
|
50
|
+
* "item.field" -> "item"
|
|
51
|
+
* "Motor_speed" -> "Motor_speed"
|
|
52
|
+
*/
|
|
53
|
+
function extractResolvedBaseIdentifier(resolvedTarget: string): string {
|
|
54
|
+
// Assumes resolvedTarget always starts with a valid identifier (not empty or starting with [ . ->)
|
|
55
|
+
// as generated by generateAssignmentTarget()
|
|
56
|
+
const bracketIndex = resolvedTarget.indexOf("[");
|
|
57
|
+
const dotIndex = resolvedTarget.indexOf(".");
|
|
58
|
+
const arrowIndex = resolvedTarget.indexOf("->");
|
|
59
|
+
|
|
60
|
+
// Find the earliest terminator
|
|
61
|
+
let endIndex = resolvedTarget.length;
|
|
62
|
+
if (bracketIndex !== -1 && bracketIndex < endIndex) {
|
|
63
|
+
endIndex = bracketIndex;
|
|
64
|
+
}
|
|
65
|
+
if (dotIndex !== -1 && dotIndex < endIndex) {
|
|
66
|
+
endIndex = dotIndex;
|
|
67
|
+
}
|
|
68
|
+
if (arrowIndex !== -1 && arrowIndex < endIndex) {
|
|
69
|
+
endIndex = arrowIndex;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return resolvedTarget.substring(0, endIndex);
|
|
73
|
+
}
|
|
74
|
+
|
|
35
75
|
/**
|
|
36
76
|
* Extract base identifier from assignment target.
|
|
37
77
|
* With unified grammar, all patterns use IDENTIFIER postfixTargetOp*.
|
|
@@ -100,6 +140,13 @@ function buildAssignmentContext(
|
|
|
100
140
|
// Generate value expression
|
|
101
141
|
const generatedValue = deps.generateExpression(valueCtx);
|
|
102
142
|
|
|
143
|
+
// Generate fully-resolved target (with scope prefixes)
|
|
144
|
+
const resolvedTarget = deps.generateAssignmentTarget(targetCtx);
|
|
145
|
+
|
|
146
|
+
// Extract resolved base identifier for type lookups
|
|
147
|
+
// Removes subscripts ([...]) and member access (. or ->) from the end
|
|
148
|
+
const resolvedBaseIdentifier = extractResolvedBaseIdentifier(resolvedTarget);
|
|
149
|
+
|
|
103
150
|
// Extract target info
|
|
104
151
|
const hasGlobal = targetCtx.GLOBAL() !== null;
|
|
105
152
|
const hasThis = targetCtx.THIS() !== null;
|
|
@@ -152,6 +199,8 @@ function buildAssignmentContext(
|
|
|
152
199
|
cOp,
|
|
153
200
|
isCompound,
|
|
154
201
|
generatedValue,
|
|
202
|
+
resolvedTarget,
|
|
203
|
+
resolvedBaseIdentifier,
|
|
155
204
|
firstIdTypeInfo,
|
|
156
205
|
memberAccessDepth,
|
|
157
206
|
subscriptDepth,
|
|
@@ -67,6 +67,21 @@ interface IAssignmentContext {
|
|
|
67
67
|
/** Generated C expression for the value (right-hand side) */
|
|
68
68
|
readonly generatedValue: string;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Fully-resolved assignment target with scope prefixes applied.
|
|
72
|
+
* Use this instead of raw identifiers to ensure proper scope resolution.
|
|
73
|
+
* Example: "data" inside scope ArrayBug -> "ArrayBug_data"
|
|
74
|
+
*/
|
|
75
|
+
readonly resolvedTarget: string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolved base identifier for type lookups.
|
|
79
|
+
* Extracted from resolvedTarget by removing subscripts and member access.
|
|
80
|
+
* Example: "ArrayBug_data[0]" -> "ArrayBug_data"
|
|
81
|
+
* Use this for CodeGenState.typeRegistry lookups instead of identifiers[0].
|
|
82
|
+
*/
|
|
83
|
+
readonly resolvedBaseIdentifier: string;
|
|
84
|
+
|
|
70
85
|
// === Type info (looked up from registry) ===
|
|
71
86
|
|
|
72
87
|
/** First identifier's type info, if found */
|
|
@@ -15,6 +15,11 @@ import TTypeInfo from "../../types/TTypeInfo";
|
|
|
15
15
|
function createMockContext(
|
|
16
16
|
overrides: Partial<IAssignmentContext> = {},
|
|
17
17
|
): IAssignmentContext {
|
|
18
|
+
// Compute resolvedBaseIdentifier from resolvedTarget if not explicitly provided
|
|
19
|
+
const resolvedTarget = overrides.resolvedTarget ?? "x";
|
|
20
|
+
const resolvedBaseIdentifier =
|
|
21
|
+
overrides.resolvedBaseIdentifier ?? resolvedTarget.split(/[[.]/)[0];
|
|
22
|
+
|
|
18
23
|
return {
|
|
19
24
|
statementCtx: {} as IAssignmentContext["statementCtx"],
|
|
20
25
|
targetCtx: {} as IAssignmentContext["targetCtx"],
|
|
@@ -31,6 +36,8 @@ function createMockContext(
|
|
|
31
36
|
cOp: "=",
|
|
32
37
|
isCompound: false,
|
|
33
38
|
generatedValue: "5",
|
|
39
|
+
resolvedTarget,
|
|
40
|
+
resolvedBaseIdentifier,
|
|
34
41
|
firstIdTypeInfo: null,
|
|
35
42
|
memberAccessDepth: 0,
|
|
36
43
|
subscriptDepth: 0,
|
|
@@ -141,7 +148,7 @@ describe("AssignmentClassifier - Bitmap Fields", () => {
|
|
|
141
148
|
["StatusFlags", new Map([["Running", { offset: 0, width: 1 }]])],
|
|
142
149
|
]);
|
|
143
150
|
setupSymbols({ bitmapFields });
|
|
144
|
-
CodeGenState.
|
|
151
|
+
CodeGenState.setVariableTypeInfo(
|
|
145
152
|
"flags",
|
|
146
153
|
createTypeInfo({ isBitmap: true, bitmapTypeName: "StatusFlags" }),
|
|
147
154
|
);
|
|
@@ -162,7 +169,7 @@ describe("AssignmentClassifier - Bitmap Fields", () => {
|
|
|
162
169
|
["StatusFlags", new Map([["Mode", { offset: 4, width: 4 }]])],
|
|
163
170
|
]);
|
|
164
171
|
setupSymbols({ bitmapFields });
|
|
165
|
-
CodeGenState.
|
|
172
|
+
CodeGenState.setVariableTypeInfo(
|
|
166
173
|
"flags",
|
|
167
174
|
createTypeInfo({ isBitmap: true, bitmapTypeName: "StatusFlags" }),
|
|
168
175
|
);
|
|
@@ -206,7 +213,7 @@ describe("AssignmentClassifier - Bitmap Fields", () => {
|
|
|
206
213
|
["Device", new Map([["flags", "DeviceFlags"]])],
|
|
207
214
|
]);
|
|
208
215
|
setupSymbols({ bitmapFields, knownStructs, structFields });
|
|
209
|
-
CodeGenState.
|
|
216
|
+
CodeGenState.setVariableTypeInfo(
|
|
210
217
|
"device",
|
|
211
218
|
createTypeInfo({ baseType: "Device" }),
|
|
212
219
|
);
|
|
@@ -233,7 +240,10 @@ describe("AssignmentClassifier - Integer Bit Access", () => {
|
|
|
233
240
|
});
|
|
234
241
|
|
|
235
242
|
it("classifies single bit access on integer", () => {
|
|
236
|
-
CodeGenState.
|
|
243
|
+
CodeGenState.setVariableTypeInfo(
|
|
244
|
+
"flags",
|
|
245
|
+
createTypeInfo({ baseType: "u8" }),
|
|
246
|
+
);
|
|
237
247
|
|
|
238
248
|
const ctx = createMockContext({
|
|
239
249
|
identifiers: ["flags"],
|
|
@@ -246,7 +256,10 @@ describe("AssignmentClassifier - Integer Bit Access", () => {
|
|
|
246
256
|
});
|
|
247
257
|
|
|
248
258
|
it("classifies bit range access on integer", () => {
|
|
249
|
-
CodeGenState.
|
|
259
|
+
CodeGenState.setVariableTypeInfo(
|
|
260
|
+
"flags",
|
|
261
|
+
createTypeInfo({ baseType: "u32" }),
|
|
262
|
+
);
|
|
250
263
|
|
|
251
264
|
const ctx = createMockContext({
|
|
252
265
|
identifiers: ["flags"],
|
|
@@ -275,7 +288,7 @@ describe("AssignmentClassifier - Array Access", () => {
|
|
|
275
288
|
});
|
|
276
289
|
|
|
277
290
|
it("classifies simple array element", () => {
|
|
278
|
-
CodeGenState.
|
|
291
|
+
CodeGenState.setVariableTypeInfo(
|
|
279
292
|
"arr",
|
|
280
293
|
createTypeInfo({
|
|
281
294
|
isArray: true,
|
|
@@ -296,7 +309,7 @@ describe("AssignmentClassifier - Array Access", () => {
|
|
|
296
309
|
});
|
|
297
310
|
|
|
298
311
|
it("classifies array slice", () => {
|
|
299
|
-
CodeGenState.
|
|
312
|
+
CodeGenState.setVariableTypeInfo(
|
|
300
313
|
"buffer",
|
|
301
314
|
createTypeInfo({
|
|
302
315
|
isArray: true,
|
|
@@ -329,7 +342,7 @@ describe("AssignmentClassifier - String Assignments", () => {
|
|
|
329
342
|
});
|
|
330
343
|
|
|
331
344
|
it("classifies simple string variable", () => {
|
|
332
|
-
CodeGenState.
|
|
345
|
+
CodeGenState.setVariableTypeInfo(
|
|
333
346
|
"name",
|
|
334
347
|
createTypeInfo({
|
|
335
348
|
baseType: "string<32>",
|
|
@@ -341,7 +354,7 @@ describe("AssignmentClassifier - String Assignments", () => {
|
|
|
341
354
|
const ctx = createMockContext({
|
|
342
355
|
identifiers: ["name"],
|
|
343
356
|
isSimpleIdentifier: true,
|
|
344
|
-
firstIdTypeInfo: CodeGenState.
|
|
357
|
+
firstIdTypeInfo: CodeGenState.getVariableTypeInfo("name")!,
|
|
345
358
|
});
|
|
346
359
|
|
|
347
360
|
expect(AssignmentClassifier.classify(ctx)).toBe(
|
|
@@ -355,7 +368,7 @@ describe("AssignmentClassifier - String Assignments", () => {
|
|
|
355
368
|
["Person", new Map([["name", "string<64>"]])],
|
|
356
369
|
]);
|
|
357
370
|
setupSymbols({ knownStructs, structFields });
|
|
358
|
-
CodeGenState.
|
|
371
|
+
CodeGenState.setVariableTypeInfo(
|
|
359
372
|
"person",
|
|
360
373
|
createTypeInfo({ baseType: "Person" }),
|
|
361
374
|
);
|
|
@@ -382,7 +395,7 @@ describe("AssignmentClassifier - Special Compound", () => {
|
|
|
382
395
|
});
|
|
383
396
|
|
|
384
397
|
it("classifies atomic RMW", () => {
|
|
385
|
-
CodeGenState.
|
|
398
|
+
CodeGenState.setVariableTypeInfo(
|
|
386
399
|
"counter",
|
|
387
400
|
createTypeInfo({
|
|
388
401
|
baseType: "u32",
|
|
@@ -401,7 +414,7 @@ describe("AssignmentClassifier - Special Compound", () => {
|
|
|
401
414
|
});
|
|
402
415
|
|
|
403
416
|
it("classifies overflow clamp", () => {
|
|
404
|
-
CodeGenState.
|
|
417
|
+
CodeGenState.setVariableTypeInfo(
|
|
405
418
|
"saturated",
|
|
406
419
|
createTypeInfo({
|
|
407
420
|
baseType: "u8",
|
|
@@ -422,7 +435,7 @@ describe("AssignmentClassifier - Special Compound", () => {
|
|
|
422
435
|
});
|
|
423
436
|
|
|
424
437
|
it("does not classify float as overflow clamp", () => {
|
|
425
|
-
CodeGenState.
|
|
438
|
+
CodeGenState.setVariableTypeInfo(
|
|
426
439
|
"value",
|
|
427
440
|
createTypeInfo({
|
|
428
441
|
baseType: "f32",
|
|
@@ -641,7 +654,7 @@ describe("AssignmentClassifier - Bitmap Array Element Field", () => {
|
|
|
641
654
|
["StatusFlags", new Map([["Active", { offset: 0, width: 1 }]])],
|
|
642
655
|
]);
|
|
643
656
|
setupSymbols({ bitmapFields });
|
|
644
|
-
CodeGenState.
|
|
657
|
+
CodeGenState.setVariableTypeInfo(
|
|
645
658
|
"flagsArr",
|
|
646
659
|
createTypeInfo({
|
|
647
660
|
isBitmap: true,
|
|
@@ -675,7 +688,7 @@ describe("AssignmentClassifier - Multi-dim Array Bit Indexing", () => {
|
|
|
675
688
|
});
|
|
676
689
|
|
|
677
690
|
it("classifies matrix[i][j][bit] as ARRAY_ELEMENT_BIT", () => {
|
|
678
|
-
CodeGenState.
|
|
691
|
+
CodeGenState.setVariableTypeInfo(
|
|
679
692
|
"matrix",
|
|
680
693
|
createTypeInfo({
|
|
681
694
|
baseType: "u32",
|
|
@@ -702,7 +715,7 @@ describe("AssignmentClassifier - Multi-dim Array Bit Indexing", () => {
|
|
|
702
715
|
});
|
|
703
716
|
|
|
704
717
|
it("classifies matrix[i][j] as MULTI_DIM_ARRAY_ELEMENT", () => {
|
|
705
|
-
CodeGenState.
|
|
718
|
+
CodeGenState.setVariableTypeInfo(
|
|
706
719
|
"matrix",
|
|
707
720
|
createTypeInfo({
|
|
708
721
|
baseType: "u32",
|
|
@@ -868,7 +881,7 @@ describe("AssignmentClassifier - Member Chain", () => {
|
|
|
868
881
|
const knownStructs = new Set(["Config"]);
|
|
869
882
|
const structFields = new Map([["Config", new Map([["items", "Item"]])]]);
|
|
870
883
|
setupSymbols({ knownStructs, structFields });
|
|
871
|
-
CodeGenState.
|
|
884
|
+
CodeGenState.setVariableTypeInfo(
|
|
872
885
|
"config",
|
|
873
886
|
createTypeInfo({ baseType: "Config" }),
|
|
874
887
|
);
|
|
@@ -20,26 +20,30 @@ function gen(): ICodeGenApi {
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Handle simple array element: arr[i] <- value
|
|
23
|
+
*
|
|
24
|
+
* Uses resolvedTarget which includes scope prefix and subscript,
|
|
25
|
+
* e.g., "data[0]" inside scope ArrayBug -> "ArrayBug_data[0]"
|
|
23
26
|
*/
|
|
24
27
|
function handleArrayElement(ctx: IAssignmentContext): string {
|
|
25
|
-
|
|
26
|
-
const index = gen().generateExpression(ctx.subscripts[0]);
|
|
27
|
-
|
|
28
|
-
return `${name}[${index}] ${ctx.cOp} ${ctx.generatedValue};`;
|
|
28
|
+
return `${ctx.resolvedTarget} ${ctx.cOp} ${ctx.generatedValue};`;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Handle multi-dimensional array element: matrix[i][j] <- value
|
|
33
|
+
*
|
|
34
|
+
* Uses resolvedTarget which includes scope prefix and all subscripts.
|
|
35
|
+
* Uses resolvedBaseIdentifier for type lookups to support scoped arrays.
|
|
33
36
|
*/
|
|
34
37
|
function handleMultiDimArrayElement(ctx: IAssignmentContext): string {
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
// Use resolvedBaseIdentifier for type lookup (includes scope prefix)
|
|
39
|
+
// e.g., "ArrayBug_data" instead of "data"
|
|
40
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(ctx.resolvedBaseIdentifier);
|
|
37
41
|
|
|
38
42
|
// ADR-036: Compile-time bounds checking for constant indices
|
|
39
43
|
if (typeInfo?.arrayDimensions) {
|
|
40
44
|
const line = ctx.subscripts[0]?.start?.line ?? 0;
|
|
41
45
|
TypeValidator.checkArrayBounds(
|
|
42
|
-
|
|
46
|
+
ctx.resolvedBaseIdentifier,
|
|
43
47
|
[...typeInfo.arrayDimensions],
|
|
44
48
|
[...ctx.subscripts],
|
|
45
49
|
line,
|
|
@@ -47,11 +51,7 @@ function handleMultiDimArrayElement(ctx: IAssignmentContext): string {
|
|
|
47
51
|
);
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
.map((e) => gen().generateExpression(e))
|
|
52
|
-
.join("][");
|
|
53
|
-
|
|
54
|
-
return `${name}[${indices}] ${ctx.cOp} ${ctx.generatedValue};`;
|
|
54
|
+
return `${ctx.resolvedTarget} ${ctx.cOp} ${ctx.generatedValue};`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -69,18 +69,21 @@ function handleArraySlice(ctx: IAssignmentContext): string {
|
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
const
|
|
72
|
+
// Use resolvedBaseIdentifier for type lookup (includes scope prefix)
|
|
73
|
+
const name = ctx.resolvedBaseIdentifier;
|
|
74
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(name);
|
|
74
75
|
|
|
75
76
|
// Get line number for error messages
|
|
76
77
|
const line = ctx.subscripts[0].start?.line ?? 0;
|
|
77
78
|
|
|
78
79
|
// Validate 1D array only
|
|
79
80
|
if (typeInfo?.arrayDimensions && typeInfo.arrayDimensions.length > 1) {
|
|
81
|
+
// Use raw identifier in error message for clarity
|
|
82
|
+
const rawName = ctx.identifiers[0];
|
|
80
83
|
throw new Error(
|
|
81
84
|
`${line}:0 Error: Slice assignment is only valid on one-dimensional arrays. ` +
|
|
82
|
-
`'${
|
|
83
|
-
`Access the innermost dimension first (e.g., ${
|
|
85
|
+
`'${rawName}' has ${typeInfo.arrayDimensions.length} dimensions. ` +
|
|
86
|
+
`Access the innermost dimension first (e.g., ${rawName}[index][offset, length]).`,
|
|
84
87
|
);
|
|
85
88
|
}
|
|
86
89
|
|
|
@@ -109,17 +112,21 @@ function handleArraySlice(ctx: IAssignmentContext): string {
|
|
|
109
112
|
} else if (typeInfo?.arrayDimensions?.[0]) {
|
|
110
113
|
capacity = typeInfo.arrayDimensions[0];
|
|
111
114
|
} else {
|
|
115
|
+
// Use raw identifier in error message for clarity
|
|
116
|
+
const rawName = ctx.identifiers[0];
|
|
112
117
|
throw new Error(
|
|
113
|
-
`${line}:0 Error: Cannot determine buffer size for '${
|
|
118
|
+
`${line}:0 Error: Cannot determine buffer size for '${rawName}' at compile time.`,
|
|
114
119
|
);
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
// Bounds validation
|
|
118
123
|
if (offsetValue + lengthValue > capacity) {
|
|
124
|
+
// Use raw identifier in error message for clarity
|
|
125
|
+
const rawName = ctx.identifiers[0];
|
|
119
126
|
throw new Error(
|
|
120
127
|
`${line}:0 Error: Slice assignment out of bounds: ` +
|
|
121
128
|
`offset(${offsetValue}) + length(${lengthValue}) = ${offsetValue + lengthValue} ` +
|
|
122
|
-
`exceeds buffer capacity(${capacity}) for '${
|
|
129
|
+
`exceeds buffer capacity(${capacity}) for '${rawName}'.`,
|
|
123
130
|
);
|
|
124
131
|
}
|
|
125
132
|
|