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
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* qualifiedType, userType) that each had identical enum/bitmap handling.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import CodeGenState from "../../state/CodeGenState";
|
|
10
10
|
import TOverflowBehavior from "./types/TOverflowBehavior";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -40,7 +40,6 @@ class TypeRegistrationUtils {
|
|
|
40
40
|
* Returns true if the type was a known enum and was registered.
|
|
41
41
|
*/
|
|
42
42
|
static tryRegisterEnumType(
|
|
43
|
-
typeRegistry: Map<string, TTypeInfo>,
|
|
44
43
|
symbols: ITypeSymbols,
|
|
45
44
|
options: ITypeRegistrationOptions,
|
|
46
45
|
): boolean {
|
|
@@ -48,7 +47,7 @@ class TypeRegistrationUtils {
|
|
|
48
47
|
return false;
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
CodeGenState.setVariableTypeInfo(options.name, {
|
|
52
51
|
baseType: options.baseType,
|
|
53
52
|
bitWidth: 0,
|
|
54
53
|
isArray: false,
|
|
@@ -69,7 +68,6 @@ class TypeRegistrationUtils {
|
|
|
69
68
|
* Handles both array and non-array bitmap types.
|
|
70
69
|
*/
|
|
71
70
|
static tryRegisterBitmapType(
|
|
72
|
-
typeRegistry: Map<string, TTypeInfo>,
|
|
73
71
|
symbols: ITypeSymbols,
|
|
74
72
|
options: ITypeRegistrationOptions,
|
|
75
73
|
arrayDimensions: number[] | undefined,
|
|
@@ -82,7 +80,7 @@ class TypeRegistrationUtils {
|
|
|
82
80
|
|
|
83
81
|
if (arrayDimensions && arrayDimensions.length > 0) {
|
|
84
82
|
// Bitmap array
|
|
85
|
-
|
|
83
|
+
CodeGenState.setVariableTypeInfo(options.name, {
|
|
86
84
|
baseType: options.baseType,
|
|
87
85
|
bitWidth,
|
|
88
86
|
isArray: true,
|
|
@@ -95,7 +93,7 @@ class TypeRegistrationUtils {
|
|
|
95
93
|
});
|
|
96
94
|
} else {
|
|
97
95
|
// Non-array bitmap
|
|
98
|
-
|
|
96
|
+
CodeGenState.setVariableTypeInfo(options.name, {
|
|
99
97
|
baseType: options.baseType,
|
|
100
98
|
bitWidth,
|
|
101
99
|
isArray: false,
|
|
@@ -303,7 +303,7 @@ class TypeResolver {
|
|
|
303
303
|
* Look up a variable name in the type registry and return a SuffixResult.
|
|
304
304
|
*/
|
|
305
305
|
private static resolveRegistryLookup(name: string): SuffixResult {
|
|
306
|
-
const typeInfo = CodeGenState.
|
|
306
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(name);
|
|
307
307
|
if (typeInfo) {
|
|
308
308
|
return {
|
|
309
309
|
stop: false,
|
|
@@ -356,7 +356,7 @@ class TypeResolver {
|
|
|
356
356
|
if (id) {
|
|
357
357
|
const name = id.getText();
|
|
358
358
|
const scopedName = CodeGenState.resolveIdentifier(name);
|
|
359
|
-
const typeInfo = CodeGenState.
|
|
359
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(scopedName);
|
|
360
360
|
if (typeInfo) {
|
|
361
361
|
return { baseType: typeInfo.baseType, isArray: typeInfo.isArray };
|
|
362
362
|
}
|
|
@@ -291,7 +291,7 @@ class TypeValidator {
|
|
|
291
291
|
|
|
292
292
|
const scopedName = CodeGenState.resolveIdentifier(identifier);
|
|
293
293
|
|
|
294
|
-
const typeInfo = CodeGenState.
|
|
294
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(scopedName);
|
|
295
295
|
if (typeInfo?.isConst) {
|
|
296
296
|
return `cannot assign to const variable '${identifier}'`;
|
|
297
297
|
}
|
|
@@ -305,7 +305,7 @@ class TypeValidator {
|
|
|
305
305
|
return true;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
const typeInfo = CodeGenState.
|
|
308
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(identifier);
|
|
309
309
|
if (typeInfo?.isConst) {
|
|
310
310
|
return true;
|
|
311
311
|
}
|
|
@@ -332,7 +332,7 @@ class TypeValidator {
|
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
const scopeMembers = CodeGenState.
|
|
335
|
+
const scopeMembers = CodeGenState.getScopeMembers(currentScope);
|
|
336
336
|
if (scopeMembers?.has(identifier)) {
|
|
337
337
|
throw new Error(
|
|
338
338
|
`Error: Use 'this.${identifier}' to access scope member '${identifier}' inside scope '${currentScope}'`,
|
|
@@ -366,7 +366,7 @@ class TypeValidator {
|
|
|
366
366
|
);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
const typeInfo = CodeGenState.
|
|
369
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(identifier);
|
|
370
370
|
if (typeInfo && !identifier.includes("_")) {
|
|
371
371
|
throw new Error(
|
|
372
372
|
`Error: Use 'global.${identifier}' to access global variable '${identifier}' inside scope '${currentScope}'`,
|
|
@@ -410,7 +410,7 @@ class TypeValidator {
|
|
|
410
410
|
identifier: string,
|
|
411
411
|
currentScope: string,
|
|
412
412
|
): string | null {
|
|
413
|
-
const scopeMembers = CodeGenState.
|
|
413
|
+
const scopeMembers = CodeGenState.getScopeMembers(currentScope);
|
|
414
414
|
if (scopeMembers?.has(identifier)) {
|
|
415
415
|
return `${currentScope}_${identifier}`;
|
|
416
416
|
}
|
|
@@ -428,7 +428,7 @@ class TypeValidator {
|
|
|
428
428
|
currentScope: string | null,
|
|
429
429
|
isKnownStruct: (name: string) => boolean,
|
|
430
430
|
): boolean {
|
|
431
|
-
const typeInfo = CodeGenState.
|
|
431
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(identifier);
|
|
432
432
|
if (typeInfo && !identifier.includes("_")) {
|
|
433
433
|
return true;
|
|
434
434
|
}
|
|
@@ -790,7 +790,7 @@ class TypeValidator {
|
|
|
790
790
|
return true;
|
|
791
791
|
}
|
|
792
792
|
|
|
793
|
-
const typeInfo = CodeGenState.
|
|
793
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(text);
|
|
794
794
|
if (typeInfo?.baseType === "bool") {
|
|
795
795
|
return true;
|
|
796
796
|
}
|
|
@@ -242,7 +242,7 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
242
242
|
|
|
243
243
|
// Manually set up scope context to test the resolution path
|
|
244
244
|
CodeGenState.currentScope = "Motor";
|
|
245
|
-
CodeGenState.
|
|
245
|
+
CodeGenState.setScopeMembers("Motor", new Set(["speed", "setSpeed"]));
|
|
246
246
|
|
|
247
247
|
// Now resolve should return prefixed name (line 633)
|
|
248
248
|
const resolved = generator.resolveIdentifier("speed");
|
|
@@ -253,7 +253,7 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
253
253
|
const { generator } = setupGenerator("u32 globalVar; void main() {}");
|
|
254
254
|
|
|
255
255
|
CodeGenState.currentScope = "Motor";
|
|
256
|
-
CodeGenState.
|
|
256
|
+
CodeGenState.setScopeMembers("Motor", new Set(["speed"]));
|
|
257
257
|
|
|
258
258
|
// globalVar is not in Motor scope members
|
|
259
259
|
const resolved = generator.resolveIdentifier("globalVar");
|
|
@@ -7318,12 +7318,15 @@ describe("CodeGenerator", () => {
|
|
|
7318
7318
|
expect(code).toContain("cfg.enabled = true");
|
|
7319
7319
|
});
|
|
7320
7320
|
|
|
7321
|
-
it("should throw when writing register from inside scope without global prefix", () => {
|
|
7321
|
+
it("should throw when writing register from inside scope when SHADOWED without global prefix", () => {
|
|
7322
|
+
// Issue #779: Ambiguity-aware validation - only require global. when shadowed
|
|
7322
7323
|
const source = `
|
|
7323
7324
|
register GPIO @ 0x40000000 {
|
|
7324
7325
|
DR: u32 rw @ 0x00,
|
|
7325
7326
|
}
|
|
7326
7327
|
scope Motor {
|
|
7328
|
+
// Shadow the register name with a scope member
|
|
7329
|
+
u32 GPIO <- 0;
|
|
7327
7330
|
public void init() {
|
|
7328
7331
|
GPIO.DR <- 0xFF;
|
|
7329
7332
|
}
|
|
@@ -7342,6 +7345,31 @@ describe("CodeGenerator", () => {
|
|
|
7342
7345
|
).toThrow("Use 'global.GPIO.DR' to access register");
|
|
7343
7346
|
});
|
|
7344
7347
|
|
|
7348
|
+
it("should allow bare register access from inside scope when NOT shadowed", () => {
|
|
7349
|
+
// Issue #779: Ambiguity-aware validation - allow unambiguous access
|
|
7350
|
+
const source = `
|
|
7351
|
+
register GPIO @ 0x40000000 {
|
|
7352
|
+
DR: u32 rw @ 0x00,
|
|
7353
|
+
}
|
|
7354
|
+
scope Motor {
|
|
7355
|
+
public void init() {
|
|
7356
|
+
GPIO.DR <- 0xFF;
|
|
7357
|
+
}
|
|
7358
|
+
}
|
|
7359
|
+
`;
|
|
7360
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
7361
|
+
const generator = new CodeGenerator();
|
|
7362
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
7363
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
7364
|
+
const code = generator.generate(tree, tokenStream, {
|
|
7365
|
+
symbolInfo: symbols,
|
|
7366
|
+
sourcePath: "test.cnx",
|
|
7367
|
+
});
|
|
7368
|
+
|
|
7369
|
+
// Should generate GPIO_DR without error since GPIO is unambiguous
|
|
7370
|
+
expect(code).toContain("GPIO_DR = 0xFF");
|
|
7371
|
+
});
|
|
7372
|
+
|
|
7345
7373
|
it("should generate scope member write from outside any scope", () => {
|
|
7346
7374
|
const source = `
|
|
7347
7375
|
scope Timer {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Tests the extracted enum/bitmap type registration helpers.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
7
7
|
import TypeRegistrationUtils from "../TypeRegistrationUtils";
|
|
8
|
-
import
|
|
8
|
+
import CodeGenState from "../../../state/CodeGenState";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Create mock symbols for testing
|
|
@@ -20,49 +20,43 @@ function createMockSymbols(overrides: Record<string, unknown> = {}) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
describe("TypeRegistrationUtils", () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
CodeGenState.reset();
|
|
25
|
+
});
|
|
26
|
+
|
|
23
27
|
describe("tryRegisterEnumType", () => {
|
|
24
28
|
it("returns false if type is not a known enum", () => {
|
|
25
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
26
29
|
const symbols = createMockSymbols();
|
|
27
30
|
|
|
28
|
-
const result = TypeRegistrationUtils.tryRegisterEnumType(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
overflowBehavior: "clamp",
|
|
36
|
-
isAtomic: false,
|
|
37
|
-
},
|
|
38
|
-
);
|
|
31
|
+
const result = TypeRegistrationUtils.tryRegisterEnumType(symbols, {
|
|
32
|
+
name: "myVar",
|
|
33
|
+
baseType: "UnknownType",
|
|
34
|
+
isConst: false,
|
|
35
|
+
overflowBehavior: "clamp",
|
|
36
|
+
isAtomic: false,
|
|
37
|
+
});
|
|
39
38
|
|
|
40
39
|
expect(result).toBe(false);
|
|
41
|
-
expect(
|
|
40
|
+
expect(CodeGenState.getVariableTypeInfo("myVar")).toBeUndefined();
|
|
42
41
|
});
|
|
43
42
|
|
|
44
43
|
it("registers enum type and returns true", () => {
|
|
45
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
46
44
|
const symbols = createMockSymbols({
|
|
47
45
|
knownEnums: new Set(["MyEnum"]),
|
|
48
46
|
});
|
|
49
47
|
|
|
50
|
-
const result = TypeRegistrationUtils.tryRegisterEnumType(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
overflowBehavior: "clamp",
|
|
58
|
-
isAtomic: false,
|
|
59
|
-
},
|
|
60
|
-
);
|
|
48
|
+
const result = TypeRegistrationUtils.tryRegisterEnumType(symbols, {
|
|
49
|
+
name: "myVar",
|
|
50
|
+
baseType: "MyEnum",
|
|
51
|
+
isConst: false,
|
|
52
|
+
overflowBehavior: "clamp",
|
|
53
|
+
isAtomic: false,
|
|
54
|
+
});
|
|
61
55
|
|
|
62
56
|
expect(result).toBe(true);
|
|
63
|
-
expect(
|
|
57
|
+
expect(CodeGenState.hasVariableTypeInfo("myVar")).toBe(true);
|
|
64
58
|
|
|
65
|
-
const info =
|
|
59
|
+
const info = CodeGenState.getVariableTypeInfo("myVar")!;
|
|
66
60
|
expect(info.baseType).toBe("MyEnum");
|
|
67
61
|
expect(info.isEnum).toBe(true);
|
|
68
62
|
expect(info.enumTypeName).toBe("MyEnum");
|
|
@@ -70,12 +64,11 @@ describe("TypeRegistrationUtils", () => {
|
|
|
70
64
|
});
|
|
71
65
|
|
|
72
66
|
it("respects isConst flag", () => {
|
|
73
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
74
67
|
const symbols = createMockSymbols({
|
|
75
68
|
knownEnums: new Set(["MyEnum"]),
|
|
76
69
|
});
|
|
77
70
|
|
|
78
|
-
TypeRegistrationUtils.tryRegisterEnumType(
|
|
71
|
+
TypeRegistrationUtils.tryRegisterEnumType(symbols, {
|
|
79
72
|
name: "myVar",
|
|
80
73
|
baseType: "MyEnum",
|
|
81
74
|
isConst: true,
|
|
@@ -83,16 +76,15 @@ describe("TypeRegistrationUtils", () => {
|
|
|
83
76
|
isAtomic: false,
|
|
84
77
|
});
|
|
85
78
|
|
|
86
|
-
expect(
|
|
79
|
+
expect(CodeGenState.getVariableTypeInfo("myVar")!.isConst).toBe(true);
|
|
87
80
|
});
|
|
88
81
|
|
|
89
82
|
it("respects overflowBehavior", () => {
|
|
90
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
91
83
|
const symbols = createMockSymbols({
|
|
92
84
|
knownEnums: new Set(["MyEnum"]),
|
|
93
85
|
});
|
|
94
86
|
|
|
95
|
-
TypeRegistrationUtils.tryRegisterEnumType(
|
|
87
|
+
TypeRegistrationUtils.tryRegisterEnumType(symbols, {
|
|
96
88
|
name: "myVar",
|
|
97
89
|
baseType: "MyEnum",
|
|
98
90
|
isConst: false,
|
|
@@ -100,16 +92,17 @@ describe("TypeRegistrationUtils", () => {
|
|
|
100
92
|
isAtomic: false,
|
|
101
93
|
});
|
|
102
94
|
|
|
103
|
-
expect(
|
|
95
|
+
expect(CodeGenState.getVariableTypeInfo("myVar")!.overflowBehavior).toBe(
|
|
96
|
+
"wrap",
|
|
97
|
+
);
|
|
104
98
|
});
|
|
105
99
|
|
|
106
100
|
it("respects isAtomic flag", () => {
|
|
107
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
108
101
|
const symbols = createMockSymbols({
|
|
109
102
|
knownEnums: new Set(["MyEnum"]),
|
|
110
103
|
});
|
|
111
104
|
|
|
112
|
-
TypeRegistrationUtils.tryRegisterEnumType(
|
|
105
|
+
TypeRegistrationUtils.tryRegisterEnumType(symbols, {
|
|
113
106
|
name: "myVar",
|
|
114
107
|
baseType: "MyEnum",
|
|
115
108
|
isConst: false,
|
|
@@ -117,17 +110,15 @@ describe("TypeRegistrationUtils", () => {
|
|
|
117
110
|
isAtomic: true,
|
|
118
111
|
});
|
|
119
112
|
|
|
120
|
-
expect(
|
|
113
|
+
expect(CodeGenState.getVariableTypeInfo("myVar")!.isAtomic).toBe(true);
|
|
121
114
|
});
|
|
122
115
|
});
|
|
123
116
|
|
|
124
117
|
describe("tryRegisterBitmapType", () => {
|
|
125
118
|
it("returns false if type is not a known bitmap", () => {
|
|
126
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
127
119
|
const symbols = createMockSymbols();
|
|
128
120
|
|
|
129
121
|
const result = TypeRegistrationUtils.tryRegisterBitmapType(
|
|
130
|
-
typeRegistry,
|
|
131
122
|
symbols,
|
|
132
123
|
{
|
|
133
124
|
name: "myVar",
|
|
@@ -140,18 +131,16 @@ describe("TypeRegistrationUtils", () => {
|
|
|
140
131
|
);
|
|
141
132
|
|
|
142
133
|
expect(result).toBe(false);
|
|
143
|
-
expect(
|
|
134
|
+
expect(CodeGenState.getVariableTypeInfo("myVar")).toBeUndefined();
|
|
144
135
|
});
|
|
145
136
|
|
|
146
137
|
it("registers non-array bitmap type", () => {
|
|
147
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
148
138
|
const symbols = createMockSymbols({
|
|
149
139
|
knownBitmaps: new Set(["MyFlags"]),
|
|
150
140
|
bitmapBitWidth: new Map([["MyFlags", 8]]),
|
|
151
141
|
});
|
|
152
142
|
|
|
153
143
|
const result = TypeRegistrationUtils.tryRegisterBitmapType(
|
|
154
|
-
typeRegistry,
|
|
155
144
|
symbols,
|
|
156
145
|
{
|
|
157
146
|
name: "flags",
|
|
@@ -164,9 +153,9 @@ describe("TypeRegistrationUtils", () => {
|
|
|
164
153
|
);
|
|
165
154
|
|
|
166
155
|
expect(result).toBe(true);
|
|
167
|
-
expect(
|
|
156
|
+
expect(CodeGenState.hasVariableTypeInfo("flags")).toBe(true);
|
|
168
157
|
|
|
169
|
-
const info =
|
|
158
|
+
const info = CodeGenState.getVariableTypeInfo("flags")!;
|
|
170
159
|
expect(info.baseType).toBe("MyFlags");
|
|
171
160
|
expect(info.isBitmap).toBe(true);
|
|
172
161
|
expect(info.bitmapTypeName).toBe("MyFlags");
|
|
@@ -175,14 +164,12 @@ describe("TypeRegistrationUtils", () => {
|
|
|
175
164
|
});
|
|
176
165
|
|
|
177
166
|
it("registers bitmap array type with dimensions", () => {
|
|
178
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
179
167
|
const symbols = createMockSymbols({
|
|
180
168
|
knownBitmaps: new Set(["MyFlags"]),
|
|
181
169
|
bitmapBitWidth: new Map([["MyFlags", 16]]),
|
|
182
170
|
});
|
|
183
171
|
|
|
184
172
|
const result = TypeRegistrationUtils.tryRegisterBitmapType(
|
|
185
|
-
typeRegistry,
|
|
186
173
|
symbols,
|
|
187
174
|
{
|
|
188
175
|
name: "flagsArray",
|
|
@@ -196,21 +183,19 @@ describe("TypeRegistrationUtils", () => {
|
|
|
196
183
|
|
|
197
184
|
expect(result).toBe(true);
|
|
198
185
|
|
|
199
|
-
const info =
|
|
186
|
+
const info = CodeGenState.getVariableTypeInfo("flagsArray")!;
|
|
200
187
|
expect(info.isArray).toBe(true);
|
|
201
188
|
expect(info.arrayDimensions).toEqual([10, 5]);
|
|
202
189
|
expect(info.bitWidth).toBe(16);
|
|
203
190
|
});
|
|
204
191
|
|
|
205
192
|
it("defaults bitWidth to 0 if not found", () => {
|
|
206
|
-
const typeRegistry = new Map<string, TTypeInfo>();
|
|
207
193
|
const symbols = createMockSymbols({
|
|
208
194
|
knownBitmaps: new Set(["MyFlags"]),
|
|
209
195
|
// No bitWidth entry
|
|
210
196
|
});
|
|
211
197
|
|
|
212
198
|
TypeRegistrationUtils.tryRegisterBitmapType(
|
|
213
|
-
typeRegistry,
|
|
214
199
|
symbols,
|
|
215
200
|
{
|
|
216
201
|
name: "flags",
|
|
@@ -222,7 +207,7 @@ describe("TypeRegistrationUtils", () => {
|
|
|
222
207
|
undefined,
|
|
223
208
|
);
|
|
224
209
|
|
|
225
|
-
expect(
|
|
210
|
+
expect(CodeGenState.getVariableTypeInfo("flags")!.bitWidth).toBe(0);
|
|
226
211
|
});
|
|
227
212
|
});
|
|
228
213
|
});
|
|
@@ -10,15 +10,18 @@ import TTypeInfo from "../types/TTypeInfo";
|
|
|
10
10
|
|
|
11
11
|
describe("TypeResolver", () => {
|
|
12
12
|
let symbolTable: SymbolTable;
|
|
13
|
-
let typeRegistry: Map<string, TTypeInfo>;
|
|
14
13
|
|
|
15
14
|
beforeEach(() => {
|
|
16
15
|
CodeGenState.reset();
|
|
17
16
|
symbolTable = new SymbolTable();
|
|
18
|
-
typeRegistry = CodeGenState.typeRegistry;
|
|
19
17
|
CodeGenState.symbolTable = symbolTable;
|
|
20
18
|
});
|
|
21
19
|
|
|
20
|
+
/** Helper function to set type info in the registry */
|
|
21
|
+
function setTypeInfo(name: string, info: TTypeInfo): void {
|
|
22
|
+
CodeGenState.setVariableTypeInfo(name, info);
|
|
23
|
+
}
|
|
24
|
+
|
|
22
25
|
// ========================================================================
|
|
23
26
|
// Type Classification Methods
|
|
24
27
|
// ========================================================================
|
|
@@ -400,7 +403,7 @@ describe("TypeResolver", () => {
|
|
|
400
403
|
};
|
|
401
404
|
|
|
402
405
|
it("should return type for identifier in registry", () => {
|
|
403
|
-
|
|
406
|
+
setTypeInfo("myVar", {
|
|
404
407
|
baseType: "u32",
|
|
405
408
|
bitWidth: 32,
|
|
406
409
|
isArray: false,
|
|
@@ -612,7 +615,7 @@ describe("TypeResolver", () => {
|
|
|
612
615
|
});
|
|
613
616
|
|
|
614
617
|
it("should return type from simple identifier", () => {
|
|
615
|
-
|
|
618
|
+
setTypeInfo("counter", {
|
|
616
619
|
baseType: "u32",
|
|
617
620
|
bitWidth: 32,
|
|
618
621
|
isArray: false,
|
|
@@ -651,7 +654,7 @@ describe("TypeResolver", () => {
|
|
|
651
654
|
});
|
|
652
655
|
|
|
653
656
|
it("should return member type for struct member access", () => {
|
|
654
|
-
|
|
657
|
+
setTypeInfo("point", {
|
|
655
658
|
baseType: "Point",
|
|
656
659
|
bitWidth: 0,
|
|
657
660
|
isArray: false,
|
|
@@ -675,7 +678,7 @@ describe("TypeResolver", () => {
|
|
|
675
678
|
});
|
|
676
679
|
|
|
677
680
|
it("should return null for unknown member", () => {
|
|
678
|
-
|
|
681
|
+
setTypeInfo("point", {
|
|
679
682
|
baseType: "Point",
|
|
680
683
|
bitWidth: 0,
|
|
681
684
|
isArray: false,
|
|
@@ -699,7 +702,7 @@ describe("TypeResolver", () => {
|
|
|
699
702
|
});
|
|
700
703
|
|
|
701
704
|
it("should return null for range bit indexing", () => {
|
|
702
|
-
|
|
705
|
+
setTypeInfo("flags", {
|
|
703
706
|
baseType: "u32",
|
|
704
707
|
bitWidth: 32,
|
|
705
708
|
isArray: false,
|
|
@@ -722,7 +725,7 @@ describe("TypeResolver", () => {
|
|
|
722
725
|
});
|
|
723
726
|
|
|
724
727
|
it("should return bool for single bit indexing on integer", () => {
|
|
725
|
-
|
|
728
|
+
setTypeInfo("flags", {
|
|
726
729
|
baseType: "u32",
|
|
727
730
|
bitWidth: 32,
|
|
728
731
|
isArray: false,
|
|
@@ -745,7 +748,7 @@ describe("TypeResolver", () => {
|
|
|
745
748
|
});
|
|
746
749
|
|
|
747
750
|
it("should return element type for struct array member indexing", () => {
|
|
748
|
-
|
|
751
|
+
setTypeInfo("data", {
|
|
749
752
|
baseType: "Data",
|
|
750
753
|
bitWidth: 0,
|
|
751
754
|
isArray: false,
|
|
@@ -774,7 +777,7 @@ describe("TypeResolver", () => {
|
|
|
774
777
|
});
|
|
775
778
|
|
|
776
779
|
it("should return element type for direct array variable indexing", () => {
|
|
777
|
-
|
|
780
|
+
setTypeInfo("arr", {
|
|
778
781
|
baseType: "u8",
|
|
779
782
|
bitWidth: 8,
|
|
780
783
|
isArray: true,
|
|
@@ -798,7 +801,7 @@ describe("TypeResolver", () => {
|
|
|
798
801
|
});
|
|
799
802
|
|
|
800
803
|
it("should return bool for bit indexing on plain integer variable", () => {
|
|
801
|
-
|
|
804
|
+
setTypeInfo("val", {
|
|
802
805
|
baseType: "u8",
|
|
803
806
|
bitWidth: 8,
|
|
804
807
|
isArray: false,
|
|
@@ -828,7 +831,7 @@ describe("TypeResolver", () => {
|
|
|
828
831
|
|
|
829
832
|
describe("getPostfixExpressionType with global/this", () => {
|
|
830
833
|
it("should resolve global.structVar.field type", () => {
|
|
831
|
-
|
|
834
|
+
setTypeInfo("config", {
|
|
832
835
|
baseType: "TConfig",
|
|
833
836
|
bitWidth: 0,
|
|
834
837
|
isArray: false,
|
|
@@ -858,7 +861,7 @@ describe("TypeResolver", () => {
|
|
|
858
861
|
});
|
|
859
862
|
|
|
860
863
|
it("should resolve global.arrayVar[0] element type", () => {
|
|
861
|
-
|
|
864
|
+
setTypeInfo("inputs", {
|
|
862
865
|
baseType: "TInput",
|
|
863
866
|
bitWidth: 0,
|
|
864
867
|
isArray: true,
|
|
@@ -888,7 +891,7 @@ describe("TypeResolver", () => {
|
|
|
888
891
|
|
|
889
892
|
it("should resolve this.scopeVar type", () => {
|
|
890
893
|
CodeGenState.currentScope = "Motor";
|
|
891
|
-
|
|
894
|
+
setTypeInfo("Motor_speed", {
|
|
892
895
|
baseType: "u32",
|
|
893
896
|
bitWidth: 32,
|
|
894
897
|
isArray: false,
|
|
@@ -951,7 +954,7 @@ describe("TypeResolver", () => {
|
|
|
951
954
|
});
|
|
952
955
|
|
|
953
956
|
it("should resolve global.struct.enumField for enum type", () => {
|
|
954
|
-
|
|
957
|
+
setTypeInfo("input", {
|
|
955
958
|
baseType: "TInput",
|
|
956
959
|
bitWidth: 0,
|
|
957
960
|
isArray: false,
|
|
@@ -987,7 +990,7 @@ describe("TypeResolver", () => {
|
|
|
987
990
|
|
|
988
991
|
describe("getUnaryExpressionType", () => {
|
|
989
992
|
it("should return type from postfix expression", () => {
|
|
990
|
-
|
|
993
|
+
setTypeInfo("value", {
|
|
991
994
|
baseType: "i32",
|
|
992
995
|
bitWidth: 32,
|
|
993
996
|
isArray: false,
|
|
@@ -1020,7 +1023,7 @@ describe("TypeResolver", () => {
|
|
|
1020
1023
|
});
|
|
1021
1024
|
|
|
1022
1025
|
it("should recurse through unary expression chain", () => {
|
|
1023
|
-
|
|
1026
|
+
setTypeInfo("x", {
|
|
1024
1027
|
baseType: "i32",
|
|
1025
1028
|
bitWidth: 32,
|
|
1026
1029
|
isArray: false,
|
|
@@ -38,16 +38,14 @@ describe("TypeValidator.resolveBareIdentifier", () => {
|
|
|
38
38
|
|
|
39
39
|
beforeEach(() => {
|
|
40
40
|
CodeGenState.reset();
|
|
41
|
-
CodeGenState.
|
|
42
|
-
|
|
43
|
-
]);
|
|
44
|
-
CodeGenState.typeRegistry.set("globalCounter", {
|
|
41
|
+
CodeGenState.setScopeMembers("Motor", new Set(["speed", "maxSpeed"]));
|
|
42
|
+
CodeGenState.setVariableTypeInfo("globalCounter", {
|
|
45
43
|
baseType: "u32",
|
|
46
44
|
bitWidth: 32,
|
|
47
45
|
isArray: false,
|
|
48
46
|
isConst: false,
|
|
49
47
|
});
|
|
50
|
-
CodeGenState.
|
|
48
|
+
CodeGenState.setVariableTypeInfo("Motor_speed", {
|
|
51
49
|
baseType: "u32",
|
|
52
50
|
bitWidth: 32,
|
|
53
51
|
isArray: false,
|
|
@@ -148,7 +146,7 @@ describe("TypeValidator.resolveBareIdentifier", () => {
|
|
|
148
146
|
describe("resolveForMemberAccess", () => {
|
|
149
147
|
it("prefers scope name over global variable for member access", () => {
|
|
150
148
|
// Setup: global variable 'LED' exists AND scope 'LED' exists
|
|
151
|
-
CodeGenState.
|
|
149
|
+
CodeGenState.setVariableTypeInfo("LED", {
|
|
152
150
|
baseType: "u8",
|
|
153
151
|
bitWidth: 8,
|
|
154
152
|
isArray: false,
|
|
@@ -74,7 +74,7 @@ function setupState(options: SetupStateOptions = {}): void {
|
|
|
74
74
|
}
|
|
75
75
|
if (options.typeRegistry) {
|
|
76
76
|
for (const [k, v] of options.typeRegistry) {
|
|
77
|
-
CodeGenState.
|
|
77
|
+
CodeGenState.setVariableTypeInfo(k, v);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
if (options.callbackTypes) {
|
|
@@ -89,7 +89,9 @@ function setupState(options: SetupStateOptions = {}): void {
|
|
|
89
89
|
CodeGenState.currentScope = options.currentScope;
|
|
90
90
|
}
|
|
91
91
|
if (options.scopeMembers) {
|
|
92
|
-
|
|
92
|
+
for (const [scope, members] of options.scopeMembers) {
|
|
93
|
+
CodeGenState.setScopeMembers(scope, members);
|
|
94
|
+
}
|
|
93
95
|
}
|
|
94
96
|
if (options.currentParameters) {
|
|
95
97
|
CodeGenState.currentParameters = options.currentParameters;
|
|
@@ -127,7 +127,7 @@ class MemberChainAnalyzer {
|
|
|
127
127
|
ops: Parser.PostfixTargetOpContext[],
|
|
128
128
|
_subscriptsSoFar: number,
|
|
129
129
|
): { type: string; isArray: boolean } | undefined {
|
|
130
|
-
const baseTypeInfo = CodeGenState.
|
|
130
|
+
const baseTypeInfo = CodeGenState.getVariableTypeInfo(baseId);
|
|
131
131
|
if (!baseTypeInfo) {
|
|
132
132
|
return undefined;
|
|
133
133
|
}
|
|
@@ -193,7 +193,7 @@ class StringLengthCounter {
|
|
|
193
193
|
const memberName = op.IDENTIFIER()?.getText();
|
|
194
194
|
if (memberName === "length") {
|
|
195
195
|
// Check if this is a string type
|
|
196
|
-
const typeInfo = CodeGenState.
|
|
196
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(primaryId);
|
|
197
197
|
if (typeInfo?.isString) {
|
|
198
198
|
const currentCount = counts.get(primaryId) || 0;
|
|
199
199
|
counts.set(primaryId, currentCount + 1);
|