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
|
@@ -3,123 +3,150 @@
|
|
|
3
3
|
* Issue #644: C/C++ mode pattern consolidation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { describe, it, expect } from "vitest";
|
|
6
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
7
7
|
import CppModeHelper from "../CppModeHelper";
|
|
8
|
+
import CodeGenState from "../../CodeGenState";
|
|
8
9
|
|
|
9
10
|
describe("CppModeHelper", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
CodeGenState.reset();
|
|
13
|
+
});
|
|
14
|
+
|
|
10
15
|
describe("C mode (cppMode: false)", () => {
|
|
11
|
-
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
CodeGenState.cppMode = false;
|
|
18
|
+
});
|
|
12
19
|
|
|
13
20
|
it("maybeAddressOf adds & prefix", () => {
|
|
14
|
-
expect(
|
|
15
|
-
expect(
|
|
21
|
+
expect(CppModeHelper.maybeAddressOf("expr")).toBe("&expr");
|
|
22
|
+
expect(CppModeHelper.maybeAddressOf("foo.bar")).toBe("&foo.bar");
|
|
16
23
|
});
|
|
17
24
|
|
|
18
25
|
it("maybeDereference wraps in (*...)", () => {
|
|
19
|
-
expect(
|
|
20
|
-
expect(
|
|
26
|
+
expect(CppModeHelper.maybeDereference("ptr")).toBe("(*ptr)");
|
|
27
|
+
expect(CppModeHelper.maybeDereference("param")).toBe("(*param)");
|
|
21
28
|
});
|
|
22
29
|
|
|
23
30
|
it("refOrPtr returns *", () => {
|
|
24
|
-
expect(
|
|
31
|
+
expect(CppModeHelper.refOrPtr()).toBe("*");
|
|
25
32
|
});
|
|
26
33
|
|
|
27
34
|
it("memberSeparator returns ->", () => {
|
|
28
|
-
expect(
|
|
35
|
+
expect(CppModeHelper.memberSeparator()).toBe("->");
|
|
29
36
|
});
|
|
30
37
|
|
|
31
38
|
it("nullLiteral returns NULL", () => {
|
|
32
|
-
expect(
|
|
39
|
+
expect(CppModeHelper.nullLiteral()).toBe("NULL");
|
|
33
40
|
});
|
|
34
41
|
|
|
35
42
|
it("cast returns C-style cast", () => {
|
|
36
|
-
expect(
|
|
37
|
-
expect(
|
|
43
|
+
expect(CppModeHelper.cast("int", "x")).toBe("(int)x");
|
|
44
|
+
expect(CppModeHelper.cast("uint8_t", "value")).toBe("(uint8_t)value");
|
|
38
45
|
});
|
|
39
46
|
|
|
40
47
|
it("reinterpretCast returns C-style cast", () => {
|
|
41
|
-
expect(
|
|
42
|
-
expect(
|
|
48
|
+
expect(CppModeHelper.reinterpretCast("char*", "ptr")).toBe("(char*)ptr");
|
|
49
|
+
expect(CppModeHelper.reinterpretCast("uint8_t*", "buf")).toBe(
|
|
50
|
+
"(uint8_t*)buf",
|
|
51
|
+
);
|
|
43
52
|
});
|
|
44
53
|
|
|
45
54
|
it("isCppMode returns false", () => {
|
|
46
|
-
expect(
|
|
55
|
+
expect(CppModeHelper.isCppMode()).toBe(false);
|
|
47
56
|
});
|
|
48
57
|
});
|
|
49
58
|
|
|
50
59
|
describe("C++ mode (cppMode: true)", () => {
|
|
51
|
-
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
CodeGenState.cppMode = true;
|
|
62
|
+
});
|
|
52
63
|
|
|
53
64
|
it("maybeAddressOf returns expr unchanged", () => {
|
|
54
|
-
expect(
|
|
55
|
-
expect(
|
|
65
|
+
expect(CppModeHelper.maybeAddressOf("expr")).toBe("expr");
|
|
66
|
+
expect(CppModeHelper.maybeAddressOf("foo.bar")).toBe("foo.bar");
|
|
56
67
|
});
|
|
57
68
|
|
|
58
69
|
it("maybeDereference returns expr unchanged", () => {
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
70
|
+
expect(CppModeHelper.maybeDereference("ptr")).toBe("ptr");
|
|
71
|
+
expect(CppModeHelper.maybeDereference("param")).toBe("param");
|
|
61
72
|
});
|
|
62
73
|
|
|
63
74
|
it("refOrPtr returns &", () => {
|
|
64
|
-
expect(
|
|
75
|
+
expect(CppModeHelper.refOrPtr()).toBe("&");
|
|
65
76
|
});
|
|
66
77
|
|
|
67
78
|
it("memberSeparator returns .", () => {
|
|
68
|
-
expect(
|
|
79
|
+
expect(CppModeHelper.memberSeparator()).toBe(".");
|
|
69
80
|
});
|
|
70
81
|
|
|
71
82
|
it("nullLiteral returns nullptr", () => {
|
|
72
|
-
expect(
|
|
83
|
+
expect(CppModeHelper.nullLiteral()).toBe("nullptr");
|
|
73
84
|
});
|
|
74
85
|
|
|
75
86
|
it("cast returns static_cast", () => {
|
|
76
|
-
expect(
|
|
77
|
-
expect(
|
|
87
|
+
expect(CppModeHelper.cast("int", "x")).toBe("static_cast<int>(x)");
|
|
88
|
+
expect(CppModeHelper.cast("uint8_t", "value")).toBe(
|
|
78
89
|
"static_cast<uint8_t>(value)",
|
|
79
90
|
);
|
|
80
91
|
});
|
|
81
92
|
|
|
82
93
|
it("reinterpretCast returns reinterpret_cast", () => {
|
|
83
|
-
expect(
|
|
94
|
+
expect(CppModeHelper.reinterpretCast("char*", "ptr")).toBe(
|
|
84
95
|
"reinterpret_cast<char*>(ptr)",
|
|
85
96
|
);
|
|
86
|
-
expect(
|
|
97
|
+
expect(CppModeHelper.reinterpretCast("uint8_t*", "buf")).toBe(
|
|
87
98
|
"reinterpret_cast<uint8_t*>(buf)",
|
|
88
99
|
);
|
|
89
100
|
});
|
|
90
101
|
|
|
91
102
|
it("isCppMode returns true", () => {
|
|
92
|
-
expect(
|
|
103
|
+
expect(CppModeHelper.isCppMode()).toBe(true);
|
|
93
104
|
});
|
|
94
105
|
});
|
|
95
106
|
|
|
96
107
|
describe("edge cases", () => {
|
|
97
|
-
it("handles expressions with special characters", () => {
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
it("handles expressions with special characters in C mode", () => {
|
|
109
|
+
CodeGenState.cppMode = false;
|
|
110
|
+
|
|
111
|
+
// Parenthesized expressions
|
|
112
|
+
expect(CppModeHelper.maybeAddressOf("(a + b)")).toBe("&(a + b)");
|
|
113
|
+
|
|
114
|
+
// Array access
|
|
115
|
+
expect(CppModeHelper.maybeDereference("arr[0]")).toBe("(*arr[0])");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("handles expressions with special characters in C++ mode", () => {
|
|
119
|
+
CodeGenState.cppMode = true;
|
|
100
120
|
|
|
101
121
|
// Parenthesized expressions
|
|
102
|
-
expect(
|
|
103
|
-
expect(cppHelper.maybeAddressOf("(a + b)")).toBe("(a + b)");
|
|
122
|
+
expect(CppModeHelper.maybeAddressOf("(a + b)")).toBe("(a + b)");
|
|
104
123
|
|
|
105
124
|
// Array access
|
|
106
|
-
expect(
|
|
107
|
-
expect(cppHelper.maybeDereference("arr[0]")).toBe("arr[0]");
|
|
125
|
+
expect(CppModeHelper.maybeDereference("arr[0]")).toBe("arr[0]");
|
|
108
126
|
});
|
|
109
127
|
|
|
110
|
-
it("handles complex type casts", () => {
|
|
111
|
-
|
|
112
|
-
const cppHelper = new CppModeHelper({ cppMode: true });
|
|
128
|
+
it("handles complex type casts in C mode", () => {
|
|
129
|
+
CodeGenState.cppMode = false;
|
|
113
130
|
|
|
114
131
|
// Pointer to pointer
|
|
115
|
-
expect(
|
|
116
|
-
expect(cppHelper.cast("int**", "ptr")).toBe("static_cast<int**>(ptr)");
|
|
132
|
+
expect(CppModeHelper.cast("int**", "ptr")).toBe("(int**)ptr");
|
|
117
133
|
|
|
118
134
|
// Const types
|
|
119
|
-
expect(
|
|
135
|
+
expect(CppModeHelper.reinterpretCast("const char*", "str")).toBe(
|
|
120
136
|
"(const char*)str",
|
|
121
137
|
);
|
|
122
|
-
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("handles complex type casts in C++ mode", () => {
|
|
141
|
+
CodeGenState.cppMode = true;
|
|
142
|
+
|
|
143
|
+
// Pointer to pointer
|
|
144
|
+
expect(CppModeHelper.cast("int**", "ptr")).toBe(
|
|
145
|
+
"static_cast<int**>(ptr)",
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Const types
|
|
149
|
+
expect(CppModeHelper.reinterpretCast("const char*", "str")).toBe(
|
|
123
150
|
"reinterpret_cast<const char*>(str)",
|
|
124
151
|
);
|
|
125
152
|
});
|
|
@@ -2,165 +2,316 @@
|
|
|
2
2
|
* Unit tests for EnumAssignmentValidator
|
|
3
3
|
*
|
|
4
4
|
* Issue #644: Tests for the extracted enum assignment validator.
|
|
5
|
+
* Rewritten for static class pattern using CodeGenState.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
8
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
8
9
|
import EnumAssignmentValidator from "../EnumAssignmentValidator.js";
|
|
10
|
+
import CodeGenState from "../../CodeGenState.js";
|
|
11
|
+
import EnumTypeResolver from "../../resolution/EnumTypeResolver.js";
|
|
12
|
+
import ICodeGenSymbols from "../../../../types/ICodeGenSymbols.js";
|
|
9
13
|
|
|
10
14
|
describe("EnumAssignmentValidator", () => {
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
const createMockSymbols = (
|
|
16
|
+
overrides: Partial<ICodeGenSymbols> = {},
|
|
17
|
+
): ICodeGenSymbols =>
|
|
18
|
+
({
|
|
19
|
+
knownScopes: new Set(),
|
|
20
|
+
knownEnums: new Set(),
|
|
21
|
+
knownBitmaps: new Set(),
|
|
22
|
+
knownStructs: new Set(),
|
|
23
|
+
knownRegisters: new Set(),
|
|
24
|
+
bitmapBitWidth: new Map(),
|
|
25
|
+
bitmapFields: new Map(),
|
|
26
|
+
bitmapBackingType: new Map(),
|
|
27
|
+
enumMembers: new Map(),
|
|
28
|
+
structFields: new Map(),
|
|
29
|
+
structFieldArrays: new Map(),
|
|
30
|
+
structFieldDimensions: new Map(),
|
|
31
|
+
functionReturnTypes: new Map(),
|
|
32
|
+
scopeMembers: new Map(),
|
|
33
|
+
scopeMemberVisibility: new Map(),
|
|
34
|
+
scopedRegisters: new Map(),
|
|
35
|
+
registerMemberAccess: new Map(),
|
|
36
|
+
registerMemberTypes: new Map(),
|
|
37
|
+
registerBaseAddresses: new Map(),
|
|
38
|
+
registerMemberOffsets: new Map(),
|
|
39
|
+
registerMemberCTypes: new Map(),
|
|
40
|
+
scopeVariableUsage: new Map(),
|
|
41
|
+
scopePrivateConstValues: new Map(),
|
|
42
|
+
getSingleFunctionForVariable: () => null,
|
|
43
|
+
hasPublicSymbols: () => false,
|
|
44
|
+
...overrides,
|
|
45
|
+
}) as ICodeGenSymbols;
|
|
13
46
|
|
|
14
47
|
beforeEach(() => {
|
|
15
|
-
|
|
48
|
+
CodeGenState.reset();
|
|
49
|
+
});
|
|
16
50
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
getCurrentScope: vi.fn(() => null),
|
|
20
|
-
getExpressionEnumType: vi.fn(() => null),
|
|
21
|
-
isIntegerExpression: vi.fn(() => false),
|
|
22
|
-
});
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
vi.restoreAllMocks();
|
|
23
53
|
});
|
|
24
54
|
|
|
25
55
|
describe("validateEnumAssignment", () => {
|
|
26
56
|
it("does nothing for non-enum types", () => {
|
|
57
|
+
CodeGenState.symbols = createMockSymbols();
|
|
27
58
|
const expression = { getText: () => "42" } as never;
|
|
28
59
|
|
|
29
|
-
// Should not throw for non-enum type
|
|
30
60
|
expect(() =>
|
|
31
|
-
|
|
61
|
+
EnumAssignmentValidator.validateEnumAssignment("u32", expression),
|
|
32
62
|
).not.toThrow();
|
|
33
63
|
});
|
|
34
64
|
|
|
35
65
|
it("throws for assigning different enum type", () => {
|
|
36
|
-
|
|
37
|
-
knownEnums,
|
|
38
|
-
getCurrentScope: () => null,
|
|
39
|
-
getExpressionEnumType: () => "Status", // Value is Status type
|
|
40
|
-
isIntegerExpression: () => false,
|
|
66
|
+
CodeGenState.symbols = createMockSymbols({
|
|
67
|
+
knownEnums: new Set(["Color", "Status"]),
|
|
41
68
|
});
|
|
69
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue("Status");
|
|
42
70
|
|
|
43
71
|
const expression = { getText: () => "Status.OK" } as never;
|
|
44
72
|
|
|
45
73
|
expect(() =>
|
|
46
|
-
|
|
74
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
47
75
|
).toThrow("Cannot assign Status enum to Color enum");
|
|
48
76
|
});
|
|
49
77
|
|
|
50
78
|
it("throws for assigning integer to enum", () => {
|
|
51
|
-
|
|
52
|
-
knownEnums,
|
|
53
|
-
getCurrentScope: () => null,
|
|
54
|
-
getExpressionEnumType: () => null,
|
|
55
|
-
isIntegerExpression: () => true, // Expression is integer
|
|
79
|
+
CodeGenState.symbols = createMockSymbols({
|
|
80
|
+
knownEnums: new Set(["Color"]),
|
|
56
81
|
});
|
|
82
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
57
83
|
|
|
58
84
|
const expression = { getText: () => "42" } as never;
|
|
59
85
|
|
|
60
86
|
expect(() =>
|
|
61
|
-
|
|
87
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
62
88
|
).toThrow("Cannot assign integer to Color enum");
|
|
63
89
|
});
|
|
64
90
|
|
|
65
91
|
it("allows same enum type assignment", () => {
|
|
66
|
-
|
|
67
|
-
knownEnums,
|
|
68
|
-
getCurrentScope: () => null,
|
|
69
|
-
getExpressionEnumType: () => "Color", // Value is Color type
|
|
70
|
-
isIntegerExpression: () => false,
|
|
92
|
+
CodeGenState.symbols = createMockSymbols({
|
|
93
|
+
knownEnums: new Set(["Color"]),
|
|
71
94
|
});
|
|
95
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue("Color");
|
|
72
96
|
|
|
73
97
|
const expression = { getText: () => "Color.RED" } as never;
|
|
74
98
|
|
|
75
99
|
expect(() =>
|
|
76
|
-
|
|
100
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
77
101
|
).not.toThrow();
|
|
78
102
|
});
|
|
79
103
|
|
|
80
104
|
it("allows direct enum member access", () => {
|
|
105
|
+
CodeGenState.symbols = createMockSymbols({
|
|
106
|
+
knownEnums: new Set(["Color"]),
|
|
107
|
+
});
|
|
108
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
109
|
+
|
|
81
110
|
const expression = { getText: () => "Color.RED" } as never;
|
|
82
111
|
|
|
83
112
|
expect(() =>
|
|
84
|
-
|
|
113
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
85
114
|
).not.toThrow();
|
|
86
115
|
});
|
|
87
116
|
|
|
88
117
|
it("allows this.Enum.MEMBER pattern", () => {
|
|
89
|
-
|
|
118
|
+
CodeGenState.currentScope = "MyScope";
|
|
119
|
+
CodeGenState.symbols = createMockSymbols({
|
|
90
120
|
knownEnums: new Set(["MyScope_State"]),
|
|
91
|
-
getCurrentScope: () => "MyScope",
|
|
92
|
-
getExpressionEnumType: () => null,
|
|
93
|
-
isIntegerExpression: () => false,
|
|
94
121
|
});
|
|
122
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
95
123
|
|
|
96
124
|
const expression = { getText: () => "this.State.ACTIVE" } as never;
|
|
97
125
|
|
|
98
126
|
expect(() =>
|
|
99
|
-
|
|
127
|
+
EnumAssignmentValidator.validateEnumAssignment(
|
|
128
|
+
"MyScope_State",
|
|
129
|
+
expression,
|
|
130
|
+
),
|
|
100
131
|
).not.toThrow();
|
|
101
132
|
});
|
|
102
133
|
|
|
103
134
|
it("allows global.Enum.MEMBER pattern", () => {
|
|
135
|
+
CodeGenState.symbols = createMockSymbols({
|
|
136
|
+
knownEnums: new Set(["Color"]),
|
|
137
|
+
});
|
|
138
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
139
|
+
|
|
104
140
|
const expression = { getText: () => "global.Color.RED" } as never;
|
|
105
141
|
|
|
106
142
|
expect(() =>
|
|
107
|
-
|
|
143
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
108
144
|
).not.toThrow();
|
|
109
145
|
});
|
|
110
146
|
|
|
111
147
|
it("throws for global.WrongEnum.MEMBER pattern", () => {
|
|
148
|
+
CodeGenState.symbols = createMockSymbols({
|
|
149
|
+
knownEnums: new Set(["Color", "Status"]),
|
|
150
|
+
});
|
|
151
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
152
|
+
|
|
112
153
|
const expression = { getText: () => "global.Status.OK" } as never;
|
|
113
154
|
|
|
114
155
|
expect(() =>
|
|
115
|
-
|
|
156
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
116
157
|
).toThrow("Cannot assign non-enum value to Color enum");
|
|
117
158
|
});
|
|
118
159
|
|
|
160
|
+
it("allows global.structVar.enumField (non-enum parts[1])", () => {
|
|
161
|
+
CodeGenState.symbols = createMockSymbols({
|
|
162
|
+
knownEnums: new Set(["EValueId"]),
|
|
163
|
+
});
|
|
164
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
165
|
+
|
|
166
|
+
const expression = {
|
|
167
|
+
getText: () => "global.input.assignedValue",
|
|
168
|
+
} as never;
|
|
169
|
+
|
|
170
|
+
// Should NOT throw — parts[1] "input" is not a known enum
|
|
171
|
+
expect(() =>
|
|
172
|
+
EnumAssignmentValidator.validateEnumAssignment("EValueId", expression),
|
|
173
|
+
).not.toThrow();
|
|
174
|
+
});
|
|
175
|
+
|
|
119
176
|
it("allows scoped enum pattern", () => {
|
|
177
|
+
CodeGenState.symbols = createMockSymbols({
|
|
178
|
+
knownEnums: new Set(["Scope_State"]),
|
|
179
|
+
});
|
|
180
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
181
|
+
|
|
120
182
|
const expression = { getText: () => "Scope.State.ACTIVE" } as never;
|
|
121
183
|
|
|
122
184
|
expect(() =>
|
|
123
|
-
|
|
185
|
+
EnumAssignmentValidator.validateEnumAssignment(
|
|
186
|
+
"Scope_State",
|
|
187
|
+
expression,
|
|
188
|
+
),
|
|
124
189
|
).not.toThrow();
|
|
125
190
|
});
|
|
126
191
|
|
|
127
192
|
it("throws for wrong scoped enum pattern", () => {
|
|
193
|
+
CodeGenState.symbols = createMockSymbols({
|
|
194
|
+
knownEnums: new Set(["Scope_State"]),
|
|
195
|
+
});
|
|
196
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
197
|
+
|
|
128
198
|
const expression = { getText: () => "Other.State.ACTIVE" } as never;
|
|
129
199
|
|
|
130
200
|
expect(() =>
|
|
131
|
-
|
|
201
|
+
EnumAssignmentValidator.validateEnumAssignment(
|
|
202
|
+
"Scope_State",
|
|
203
|
+
expression,
|
|
204
|
+
),
|
|
132
205
|
).toThrow("Cannot assign non-enum value to Scope_State enum");
|
|
133
206
|
});
|
|
134
207
|
|
|
135
208
|
it("throws for this.WrongEnum.MEMBER with wrong scoped name", () => {
|
|
136
|
-
|
|
209
|
+
CodeGenState.currentScope = "MyScope";
|
|
210
|
+
CodeGenState.symbols = createMockSymbols({
|
|
137
211
|
knownEnums: new Set(["MyScope_State"]),
|
|
138
|
-
getCurrentScope: () => "MyScope",
|
|
139
|
-
getExpressionEnumType: () => null,
|
|
140
|
-
isIntegerExpression: () => false,
|
|
141
212
|
});
|
|
213
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
142
214
|
|
|
143
215
|
const expression = { getText: () => "this.Wrong.ACTIVE" } as never;
|
|
144
216
|
|
|
145
217
|
expect(() =>
|
|
146
|
-
|
|
218
|
+
EnumAssignmentValidator.validateEnumAssignment(
|
|
219
|
+
"MyScope_State",
|
|
220
|
+
expression,
|
|
221
|
+
),
|
|
147
222
|
).toThrow("Cannot assign non-enum value to MyScope_State enum");
|
|
148
223
|
});
|
|
149
224
|
|
|
150
225
|
it("throws for variable.field access on non-enum type", () => {
|
|
226
|
+
CodeGenState.symbols = createMockSymbols({
|
|
227
|
+
knownEnums: new Set(["Color"]),
|
|
228
|
+
});
|
|
229
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
230
|
+
|
|
151
231
|
const expression = { getText: () => "someVar.field" } as never;
|
|
152
232
|
|
|
153
233
|
expect(() =>
|
|
154
|
-
|
|
234
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
155
235
|
).toThrow("Cannot assign non-enum value to Color enum");
|
|
156
236
|
});
|
|
157
237
|
|
|
158
238
|
it("allows variable.field when variable is a known enum", () => {
|
|
239
|
+
CodeGenState.symbols = createMockSymbols({
|
|
240
|
+
knownEnums: new Set(["Color", "Status"]),
|
|
241
|
+
});
|
|
242
|
+
vi.spyOn(EnumTypeResolver, "resolve").mockReturnValue(null);
|
|
243
|
+
|
|
159
244
|
const expression = { getText: () => "Status.OK" } as never;
|
|
160
245
|
|
|
161
246
|
expect(() =>
|
|
162
|
-
|
|
247
|
+
EnumAssignmentValidator.validateEnumAssignment("Color", expression),
|
|
163
248
|
).not.toThrow();
|
|
164
249
|
});
|
|
165
250
|
});
|
|
251
|
+
|
|
252
|
+
describe("isIntegerExpression", () => {
|
|
253
|
+
it("returns true for decimal literals", () => {
|
|
254
|
+
expect(
|
|
255
|
+
EnumAssignmentValidator.isIntegerExpression({
|
|
256
|
+
getText: () => "42",
|
|
257
|
+
} as never),
|
|
258
|
+
).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("returns true for hex literals", () => {
|
|
262
|
+
expect(
|
|
263
|
+
EnumAssignmentValidator.isIntegerExpression({
|
|
264
|
+
getText: () => "0xFF",
|
|
265
|
+
} as never),
|
|
266
|
+
).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("returns true for binary literals", () => {
|
|
270
|
+
expect(
|
|
271
|
+
EnumAssignmentValidator.isIntegerExpression({
|
|
272
|
+
getText: () => "0b1010",
|
|
273
|
+
} as never),
|
|
274
|
+
).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("returns true for integer-typed variable", () => {
|
|
278
|
+
CodeGenState.typeRegistry.set("count", {
|
|
279
|
+
baseType: "u32",
|
|
280
|
+
bitWidth: 32,
|
|
281
|
+
isArray: false,
|
|
282
|
+
isConst: false,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
expect(
|
|
286
|
+
EnumAssignmentValidator.isIntegerExpression({
|
|
287
|
+
getText: () => "count",
|
|
288
|
+
} as never),
|
|
289
|
+
).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("returns false for enum-typed variable", () => {
|
|
293
|
+
CodeGenState.typeRegistry.set("state", {
|
|
294
|
+
baseType: "State",
|
|
295
|
+
bitWidth: 0,
|
|
296
|
+
isArray: false,
|
|
297
|
+
isConst: false,
|
|
298
|
+
isEnum: true,
|
|
299
|
+
enumTypeName: "State",
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
expect(
|
|
303
|
+
EnumAssignmentValidator.isIntegerExpression({
|
|
304
|
+
getText: () => "state",
|
|
305
|
+
} as never),
|
|
306
|
+
).toBe(false);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("returns false for non-integer expression", () => {
|
|
310
|
+
expect(
|
|
311
|
+
EnumAssignmentValidator.isIntegerExpression({
|
|
312
|
+
getText: () => "foo.bar",
|
|
313
|
+
} as never),
|
|
314
|
+
).toBe(false);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
166
317
|
});
|