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
|
@@ -5,26 +5,18 @@
|
|
|
5
5
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
6
6
|
import TypeResolver from "../TypeResolver";
|
|
7
7
|
import SymbolTable from "../../../logic/symbols/SymbolTable";
|
|
8
|
-
import
|
|
8
|
+
import CodeGenState from "../CodeGenState";
|
|
9
9
|
import TTypeInfo from "../types/TTypeInfo";
|
|
10
10
|
|
|
11
11
|
describe("TypeResolver", () => {
|
|
12
|
-
let resolver: TypeResolver;
|
|
13
12
|
let symbolTable: SymbolTable;
|
|
14
13
|
let typeRegistry: Map<string, TTypeInfo>;
|
|
15
14
|
|
|
16
15
|
beforeEach(() => {
|
|
16
|
+
CodeGenState.reset();
|
|
17
17
|
symbolTable = new SymbolTable();
|
|
18
|
-
typeRegistry =
|
|
19
|
-
|
|
20
|
-
const deps: ITypeResolverDeps = {
|
|
21
|
-
symbols: null,
|
|
22
|
-
symbolTable,
|
|
23
|
-
typeRegistry,
|
|
24
|
-
resolveIdentifier: (name: string) => name,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
resolver = new TypeResolver(deps);
|
|
18
|
+
typeRegistry = CodeGenState.typeRegistry;
|
|
19
|
+
CodeGenState.symbolTable = symbolTable;
|
|
28
20
|
});
|
|
29
21
|
|
|
30
22
|
// ========================================================================
|
|
@@ -33,80 +25,80 @@ describe("TypeResolver", () => {
|
|
|
33
25
|
|
|
34
26
|
describe("isIntegerType", () => {
|
|
35
27
|
it("should return true for unsigned integer types", () => {
|
|
36
|
-
expect(
|
|
37
|
-
expect(
|
|
38
|
-
expect(
|
|
39
|
-
expect(
|
|
28
|
+
expect(TypeResolver.isIntegerType("u8")).toBe(true);
|
|
29
|
+
expect(TypeResolver.isIntegerType("u16")).toBe(true);
|
|
30
|
+
expect(TypeResolver.isIntegerType("u32")).toBe(true);
|
|
31
|
+
expect(TypeResolver.isIntegerType("u64")).toBe(true);
|
|
40
32
|
});
|
|
41
33
|
|
|
42
34
|
it("should return true for signed integer types", () => {
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
45
|
-
expect(
|
|
46
|
-
expect(
|
|
35
|
+
expect(TypeResolver.isIntegerType("i8")).toBe(true);
|
|
36
|
+
expect(TypeResolver.isIntegerType("i16")).toBe(true);
|
|
37
|
+
expect(TypeResolver.isIntegerType("i32")).toBe(true);
|
|
38
|
+
expect(TypeResolver.isIntegerType("i64")).toBe(true);
|
|
47
39
|
});
|
|
48
40
|
|
|
49
41
|
it("should return false for non-integer types", () => {
|
|
50
|
-
expect(
|
|
51
|
-
expect(
|
|
52
|
-
expect(
|
|
53
|
-
expect(
|
|
54
|
-
expect(
|
|
42
|
+
expect(TypeResolver.isIntegerType("f32")).toBe(false);
|
|
43
|
+
expect(TypeResolver.isIntegerType("f64")).toBe(false);
|
|
44
|
+
expect(TypeResolver.isIntegerType("bool")).toBe(false);
|
|
45
|
+
expect(TypeResolver.isIntegerType("void")).toBe(false);
|
|
46
|
+
expect(TypeResolver.isIntegerType("MyStruct")).toBe(false);
|
|
55
47
|
});
|
|
56
48
|
});
|
|
57
49
|
|
|
58
50
|
describe("isFloatType", () => {
|
|
59
51
|
it("should return true for float types", () => {
|
|
60
|
-
expect(
|
|
61
|
-
expect(
|
|
52
|
+
expect(TypeResolver.isFloatType("f32")).toBe(true);
|
|
53
|
+
expect(TypeResolver.isFloatType("f64")).toBe(true);
|
|
62
54
|
});
|
|
63
55
|
|
|
64
56
|
it("should return false for non-float types", () => {
|
|
65
|
-
expect(
|
|
66
|
-
expect(
|
|
67
|
-
expect(
|
|
57
|
+
expect(TypeResolver.isFloatType("u32")).toBe(false);
|
|
58
|
+
expect(TypeResolver.isFloatType("i32")).toBe(false);
|
|
59
|
+
expect(TypeResolver.isFloatType("bool")).toBe(false);
|
|
68
60
|
});
|
|
69
61
|
});
|
|
70
62
|
|
|
71
63
|
describe("isSignedType", () => {
|
|
72
64
|
it("should return true for signed integer types", () => {
|
|
73
|
-
expect(
|
|
74
|
-
expect(
|
|
75
|
-
expect(
|
|
76
|
-
expect(
|
|
65
|
+
expect(TypeResolver.isSignedType("i8")).toBe(true);
|
|
66
|
+
expect(TypeResolver.isSignedType("i16")).toBe(true);
|
|
67
|
+
expect(TypeResolver.isSignedType("i32")).toBe(true);
|
|
68
|
+
expect(TypeResolver.isSignedType("i64")).toBe(true);
|
|
77
69
|
});
|
|
78
70
|
|
|
79
71
|
it("should return false for unsigned types", () => {
|
|
80
|
-
expect(
|
|
81
|
-
expect(
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
72
|
+
expect(TypeResolver.isSignedType("u8")).toBe(false);
|
|
73
|
+
expect(TypeResolver.isSignedType("u16")).toBe(false);
|
|
74
|
+
expect(TypeResolver.isSignedType("u32")).toBe(false);
|
|
75
|
+
expect(TypeResolver.isSignedType("u64")).toBe(false);
|
|
84
76
|
});
|
|
85
77
|
|
|
86
78
|
it("should return false for non-integer types", () => {
|
|
87
|
-
expect(
|
|
88
|
-
expect(
|
|
79
|
+
expect(TypeResolver.isSignedType("f32")).toBe(false);
|
|
80
|
+
expect(TypeResolver.isSignedType("bool")).toBe(false);
|
|
89
81
|
});
|
|
90
82
|
});
|
|
91
83
|
|
|
92
84
|
describe("isUnsignedType", () => {
|
|
93
85
|
it("should return true for unsigned integer types", () => {
|
|
94
|
-
expect(
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
97
|
-
expect(
|
|
86
|
+
expect(TypeResolver.isUnsignedType("u8")).toBe(true);
|
|
87
|
+
expect(TypeResolver.isUnsignedType("u16")).toBe(true);
|
|
88
|
+
expect(TypeResolver.isUnsignedType("u32")).toBe(true);
|
|
89
|
+
expect(TypeResolver.isUnsignedType("u64")).toBe(true);
|
|
98
90
|
});
|
|
99
91
|
|
|
100
92
|
it("should return false for signed types", () => {
|
|
101
|
-
expect(
|
|
102
|
-
expect(
|
|
103
|
-
expect(
|
|
104
|
-
expect(
|
|
93
|
+
expect(TypeResolver.isUnsignedType("i8")).toBe(false);
|
|
94
|
+
expect(TypeResolver.isUnsignedType("i16")).toBe(false);
|
|
95
|
+
expect(TypeResolver.isUnsignedType("i32")).toBe(false);
|
|
96
|
+
expect(TypeResolver.isUnsignedType("i64")).toBe(false);
|
|
105
97
|
});
|
|
106
98
|
|
|
107
99
|
it("should return false for non-integer types", () => {
|
|
108
|
-
expect(
|
|
109
|
-
expect(
|
|
100
|
+
expect(TypeResolver.isUnsignedType("f32")).toBe(false);
|
|
101
|
+
expect(TypeResolver.isUnsignedType("bool")).toBe(false);
|
|
110
102
|
});
|
|
111
103
|
});
|
|
112
104
|
|
|
@@ -119,16 +111,16 @@ describe("TypeResolver", () => {
|
|
|
119
111
|
symbolTable.addStructField("Point", "x", "i32");
|
|
120
112
|
symbolTable.addStructField("Point", "y", "i32");
|
|
121
113
|
|
|
122
|
-
expect(
|
|
114
|
+
expect(TypeResolver.isStructType("Point")).toBe(true);
|
|
123
115
|
});
|
|
124
116
|
|
|
125
117
|
it("should return false for unknown type", () => {
|
|
126
|
-
expect(
|
|
118
|
+
expect(TypeResolver.isStructType("UnknownStruct")).toBe(false);
|
|
127
119
|
});
|
|
128
120
|
|
|
129
121
|
it("should return false for primitive types", () => {
|
|
130
|
-
expect(
|
|
131
|
-
expect(
|
|
122
|
+
expect(TypeResolver.isStructType("u32")).toBe(false);
|
|
123
|
+
expect(TypeResolver.isStructType("f64")).toBe(false);
|
|
132
124
|
});
|
|
133
125
|
});
|
|
134
126
|
|
|
@@ -138,83 +130,99 @@ describe("TypeResolver", () => {
|
|
|
138
130
|
|
|
139
131
|
describe("isNarrowingConversion", () => {
|
|
140
132
|
it("should return true when target is smaller than source", () => {
|
|
141
|
-
expect(
|
|
142
|
-
expect(
|
|
143
|
-
expect(
|
|
144
|
-
expect(
|
|
133
|
+
expect(TypeResolver.isNarrowingConversion("u32", "u16")).toBe(true);
|
|
134
|
+
expect(TypeResolver.isNarrowingConversion("u32", "u8")).toBe(true);
|
|
135
|
+
expect(TypeResolver.isNarrowingConversion("u64", "u32")).toBe(true);
|
|
136
|
+
expect(TypeResolver.isNarrowingConversion("i64", "i8")).toBe(true);
|
|
145
137
|
});
|
|
146
138
|
|
|
147
139
|
it("should return false when target is same size or larger", () => {
|
|
148
|
-
expect(
|
|
149
|
-
expect(
|
|
150
|
-
expect(
|
|
140
|
+
expect(TypeResolver.isNarrowingConversion("u16", "u32")).toBe(false);
|
|
141
|
+
expect(TypeResolver.isNarrowingConversion("u32", "u32")).toBe(false);
|
|
142
|
+
expect(TypeResolver.isNarrowingConversion("u8", "u64")).toBe(false);
|
|
151
143
|
});
|
|
152
144
|
|
|
153
145
|
it("should return false for unknown types", () => {
|
|
154
|
-
expect(
|
|
155
|
-
expect(
|
|
146
|
+
expect(TypeResolver.isNarrowingConversion("unknown", "u32")).toBe(false);
|
|
147
|
+
expect(TypeResolver.isNarrowingConversion("u32", "unknown")).toBe(false);
|
|
156
148
|
});
|
|
157
149
|
});
|
|
158
150
|
|
|
159
151
|
describe("isSignConversion", () => {
|
|
160
152
|
it("should return true for signed to unsigned conversion", () => {
|
|
161
|
-
expect(
|
|
162
|
-
expect(
|
|
153
|
+
expect(TypeResolver.isSignConversion("i32", "u32")).toBe(true);
|
|
154
|
+
expect(TypeResolver.isSignConversion("i8", "u64")).toBe(true);
|
|
163
155
|
});
|
|
164
156
|
|
|
165
157
|
it("should return true for unsigned to signed conversion", () => {
|
|
166
|
-
expect(
|
|
167
|
-
expect(
|
|
158
|
+
expect(TypeResolver.isSignConversion("u32", "i32")).toBe(true);
|
|
159
|
+
expect(TypeResolver.isSignConversion("u8", "i64")).toBe(true);
|
|
168
160
|
});
|
|
169
161
|
|
|
170
162
|
it("should return false for same-sign conversion", () => {
|
|
171
|
-
expect(
|
|
172
|
-
expect(
|
|
163
|
+
expect(TypeResolver.isSignConversion("i32", "i64")).toBe(false);
|
|
164
|
+
expect(TypeResolver.isSignConversion("u32", "u64")).toBe(false);
|
|
173
165
|
});
|
|
174
166
|
|
|
175
167
|
it("should return false for non-integer types", () => {
|
|
176
|
-
expect(
|
|
177
|
-
expect(
|
|
168
|
+
expect(TypeResolver.isSignConversion("f32", "f64")).toBe(false);
|
|
169
|
+
expect(TypeResolver.isSignConversion("bool", "u8")).toBe(false);
|
|
178
170
|
});
|
|
179
171
|
});
|
|
180
172
|
|
|
181
173
|
describe("validateTypeConversion", () => {
|
|
182
174
|
it("should not throw for same type", () => {
|
|
183
|
-
expect(() =>
|
|
184
|
-
|
|
175
|
+
expect(() =>
|
|
176
|
+
TypeResolver.validateTypeConversion("u32", "u32"),
|
|
177
|
+
).not.toThrow();
|
|
178
|
+
expect(() =>
|
|
179
|
+
TypeResolver.validateTypeConversion("i64", "i64"),
|
|
180
|
+
).not.toThrow();
|
|
185
181
|
});
|
|
186
182
|
|
|
187
183
|
it("should not throw for widening conversion", () => {
|
|
188
|
-
expect(() =>
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
expect(() =>
|
|
185
|
+
TypeResolver.validateTypeConversion("u32", "u16"),
|
|
186
|
+
).not.toThrow();
|
|
187
|
+
expect(() =>
|
|
188
|
+
TypeResolver.validateTypeConversion("u64", "u8"),
|
|
189
|
+
).not.toThrow();
|
|
190
|
+
expect(() =>
|
|
191
|
+
TypeResolver.validateTypeConversion("i64", "i32"),
|
|
192
|
+
).not.toThrow();
|
|
191
193
|
});
|
|
192
194
|
|
|
193
195
|
it("should throw for narrowing conversion", () => {
|
|
194
|
-
expect(() =>
|
|
196
|
+
expect(() => TypeResolver.validateTypeConversion("u8", "u32")).toThrow(
|
|
195
197
|
/narrowing/,
|
|
196
198
|
);
|
|
197
|
-
expect(() =>
|
|
199
|
+
expect(() => TypeResolver.validateTypeConversion("u16", "u64")).toThrow(
|
|
198
200
|
/narrowing/,
|
|
199
201
|
);
|
|
200
202
|
});
|
|
201
203
|
|
|
202
204
|
it("should throw for sign conversion", () => {
|
|
203
|
-
expect(() =>
|
|
205
|
+
expect(() => TypeResolver.validateTypeConversion("u32", "i32")).toThrow(
|
|
204
206
|
/sign change/,
|
|
205
207
|
);
|
|
206
|
-
expect(() =>
|
|
208
|
+
expect(() => TypeResolver.validateTypeConversion("i32", "u32")).toThrow(
|
|
207
209
|
/sign change/,
|
|
208
210
|
);
|
|
209
211
|
});
|
|
210
212
|
|
|
211
213
|
it("should not throw when source type is null", () => {
|
|
212
|
-
expect(() =>
|
|
214
|
+
expect(() =>
|
|
215
|
+
TypeResolver.validateTypeConversion("u32", null),
|
|
216
|
+
).not.toThrow();
|
|
213
217
|
});
|
|
214
218
|
|
|
215
219
|
it("should not throw for non-integer types", () => {
|
|
216
|
-
expect(() =>
|
|
217
|
-
|
|
220
|
+
expect(() =>
|
|
221
|
+
TypeResolver.validateTypeConversion("f32", "f64"),
|
|
222
|
+
).not.toThrow();
|
|
223
|
+
expect(() =>
|
|
224
|
+
TypeResolver.validateTypeConversion("bool", "u8"),
|
|
225
|
+
).not.toThrow();
|
|
218
226
|
});
|
|
219
227
|
});
|
|
220
228
|
|
|
@@ -225,60 +233,62 @@ describe("TypeResolver", () => {
|
|
|
225
233
|
describe("validateLiteralFitsType", () => {
|
|
226
234
|
describe("unsigned types", () => {
|
|
227
235
|
it("should accept valid u8 values", () => {
|
|
228
|
-
expect(() => resolver.validateLiteralFitsType("0", "u8")).not.toThrow();
|
|
229
236
|
expect(() =>
|
|
230
|
-
|
|
237
|
+
TypeResolver.validateLiteralFitsType("0", "u8"),
|
|
238
|
+
).not.toThrow();
|
|
239
|
+
expect(() =>
|
|
240
|
+
TypeResolver.validateLiteralFitsType("255", "u8"),
|
|
231
241
|
).not.toThrow();
|
|
232
242
|
expect(() =>
|
|
233
|
-
|
|
243
|
+
TypeResolver.validateLiteralFitsType("0xFF", "u8"),
|
|
234
244
|
).not.toThrow();
|
|
235
245
|
});
|
|
236
246
|
|
|
237
247
|
it("should reject out-of-range u8 values", () => {
|
|
238
|
-
expect(() =>
|
|
239
|
-
/exceeds u8 range/,
|
|
240
|
-
);
|
|
241
|
-
expect(() => resolver.validateLiteralFitsType("1000", "u8")).toThrow(
|
|
248
|
+
expect(() => TypeResolver.validateLiteralFitsType("256", "u8")).toThrow(
|
|
242
249
|
/exceeds u8 range/,
|
|
243
250
|
);
|
|
251
|
+
expect(() =>
|
|
252
|
+
TypeResolver.validateLiteralFitsType("1000", "u8"),
|
|
253
|
+
).toThrow(/exceeds u8 range/);
|
|
244
254
|
});
|
|
245
255
|
|
|
246
256
|
it("should reject negative values for unsigned types", () => {
|
|
247
|
-
expect(() =>
|
|
248
|
-
/Negative value/,
|
|
249
|
-
);
|
|
250
|
-
expect(() => resolver.validateLiteralFitsType("-100", "u32")).toThrow(
|
|
257
|
+
expect(() => TypeResolver.validateLiteralFitsType("-1", "u8")).toThrow(
|
|
251
258
|
/Negative value/,
|
|
252
259
|
);
|
|
260
|
+
expect(() =>
|
|
261
|
+
TypeResolver.validateLiteralFitsType("-100", "u32"),
|
|
262
|
+
).toThrow(/Negative value/);
|
|
253
263
|
});
|
|
254
264
|
|
|
255
265
|
it("should accept valid u16 values", () => {
|
|
256
266
|
expect(() =>
|
|
257
|
-
|
|
267
|
+
TypeResolver.validateLiteralFitsType("65535", "u16"),
|
|
258
268
|
).not.toThrow();
|
|
259
269
|
expect(() =>
|
|
260
|
-
|
|
270
|
+
TypeResolver.validateLiteralFitsType("0xFFFF", "u16"),
|
|
261
271
|
).not.toThrow();
|
|
262
272
|
});
|
|
263
273
|
|
|
264
274
|
it("should reject out-of-range u16 values", () => {
|
|
265
|
-
expect(() =>
|
|
266
|
-
|
|
267
|
-
);
|
|
275
|
+
expect(() =>
|
|
276
|
+
TypeResolver.validateLiteralFitsType("65536", "u16"),
|
|
277
|
+
).toThrow(/exceeds u16 range/);
|
|
268
278
|
});
|
|
269
279
|
|
|
270
280
|
it("should accept valid u32 values", () => {
|
|
271
281
|
expect(() =>
|
|
272
|
-
|
|
282
|
+
TypeResolver.validateLiteralFitsType("4294967295", "u32"),
|
|
273
283
|
).not.toThrow();
|
|
274
284
|
expect(() =>
|
|
275
|
-
|
|
285
|
+
TypeResolver.validateLiteralFitsType("0xFFFFFFFF", "u32"),
|
|
276
286
|
).not.toThrow();
|
|
277
287
|
});
|
|
278
288
|
|
|
279
289
|
it("should reject out-of-range u32 values", () => {
|
|
280
290
|
expect(() =>
|
|
281
|
-
|
|
291
|
+
TypeResolver.validateLiteralFitsType("4294967296", "u32"),
|
|
282
292
|
).toThrow(/exceeds u32 range/);
|
|
283
293
|
});
|
|
284
294
|
});
|
|
@@ -286,38 +296,40 @@ describe("TypeResolver", () => {
|
|
|
286
296
|
describe("signed types", () => {
|
|
287
297
|
it("should accept valid i8 values", () => {
|
|
288
298
|
expect(() =>
|
|
289
|
-
|
|
299
|
+
TypeResolver.validateLiteralFitsType("-128", "i8"),
|
|
300
|
+
).not.toThrow();
|
|
301
|
+
expect(() =>
|
|
302
|
+
TypeResolver.validateLiteralFitsType("127", "i8"),
|
|
290
303
|
).not.toThrow();
|
|
291
304
|
expect(() =>
|
|
292
|
-
|
|
305
|
+
TypeResolver.validateLiteralFitsType("0", "i8"),
|
|
293
306
|
).not.toThrow();
|
|
294
|
-
expect(() => resolver.validateLiteralFitsType("0", "i8")).not.toThrow();
|
|
295
307
|
});
|
|
296
308
|
|
|
297
309
|
it("should reject out-of-range i8 values", () => {
|
|
298
|
-
expect(() =>
|
|
299
|
-
/exceeds i8 range/,
|
|
300
|
-
);
|
|
301
|
-
expect(() => resolver.validateLiteralFitsType("-129", "i8")).toThrow(
|
|
310
|
+
expect(() => TypeResolver.validateLiteralFitsType("128", "i8")).toThrow(
|
|
302
311
|
/exceeds i8 range/,
|
|
303
312
|
);
|
|
313
|
+
expect(() =>
|
|
314
|
+
TypeResolver.validateLiteralFitsType("-129", "i8"),
|
|
315
|
+
).toThrow(/exceeds i8 range/);
|
|
304
316
|
});
|
|
305
317
|
|
|
306
318
|
it("should accept valid i32 values", () => {
|
|
307
319
|
expect(() =>
|
|
308
|
-
|
|
320
|
+
TypeResolver.validateLiteralFitsType("-2147483648", "i32"),
|
|
309
321
|
).not.toThrow();
|
|
310
322
|
expect(() =>
|
|
311
|
-
|
|
323
|
+
TypeResolver.validateLiteralFitsType("2147483647", "i32"),
|
|
312
324
|
).not.toThrow();
|
|
313
325
|
});
|
|
314
326
|
|
|
315
327
|
it("should reject out-of-range i32 values", () => {
|
|
316
328
|
expect(() =>
|
|
317
|
-
|
|
329
|
+
TypeResolver.validateLiteralFitsType("2147483648", "i32"),
|
|
318
330
|
).toThrow(/exceeds i32 range/);
|
|
319
331
|
expect(() =>
|
|
320
|
-
|
|
332
|
+
TypeResolver.validateLiteralFitsType("-2147483649", "i32"),
|
|
321
333
|
).toThrow(/exceeds i32 range/);
|
|
322
334
|
});
|
|
323
335
|
});
|
|
@@ -325,19 +337,19 @@ describe("TypeResolver", () => {
|
|
|
325
337
|
describe("hex and binary literals", () => {
|
|
326
338
|
it("should validate hex literals", () => {
|
|
327
339
|
expect(() =>
|
|
328
|
-
|
|
340
|
+
TypeResolver.validateLiteralFitsType("0xFF", "u8"),
|
|
329
341
|
).not.toThrow();
|
|
330
|
-
expect(() =>
|
|
331
|
-
|
|
332
|
-
);
|
|
342
|
+
expect(() =>
|
|
343
|
+
TypeResolver.validateLiteralFitsType("0x100", "u8"),
|
|
344
|
+
).toThrow(/exceeds u8 range/);
|
|
333
345
|
});
|
|
334
346
|
|
|
335
347
|
it("should validate binary literals", () => {
|
|
336
348
|
expect(() =>
|
|
337
|
-
|
|
349
|
+
TypeResolver.validateLiteralFitsType("0b11111111", "u8"),
|
|
338
350
|
).not.toThrow();
|
|
339
351
|
expect(() =>
|
|
340
|
-
|
|
352
|
+
TypeResolver.validateLiteralFitsType("0b100000000", "u8"),
|
|
341
353
|
).toThrow(/exceeds u8 range/);
|
|
342
354
|
});
|
|
343
355
|
});
|
|
@@ -345,19 +357,19 @@ describe("TypeResolver", () => {
|
|
|
345
357
|
describe("edge cases", () => {
|
|
346
358
|
it("should skip validation for unknown types", () => {
|
|
347
359
|
expect(() =>
|
|
348
|
-
|
|
360
|
+
TypeResolver.validateLiteralFitsType("999999", "unknown"),
|
|
349
361
|
).not.toThrow();
|
|
350
362
|
expect(() =>
|
|
351
|
-
|
|
363
|
+
TypeResolver.validateLiteralFitsType("999999", "f32"),
|
|
352
364
|
).not.toThrow();
|
|
353
365
|
});
|
|
354
366
|
|
|
355
367
|
it("should skip validation for non-integer literals", () => {
|
|
356
368
|
expect(() =>
|
|
357
|
-
|
|
369
|
+
TypeResolver.validateLiteralFitsType("3.14", "u8"),
|
|
358
370
|
).not.toThrow();
|
|
359
371
|
expect(() =>
|
|
360
|
-
|
|
372
|
+
TypeResolver.validateLiteralFitsType("hello", "u8"),
|
|
361
373
|
).not.toThrow();
|
|
362
374
|
});
|
|
363
375
|
});
|
|
@@ -368,7 +380,6 @@ describe("TypeResolver", () => {
|
|
|
368
380
|
// ========================================================================
|
|
369
381
|
|
|
370
382
|
describe("getPrimaryExpressionType", () => {
|
|
371
|
-
// Helper to create mock primary expression context
|
|
372
383
|
const mockPrimary = (opts: {
|
|
373
384
|
identifier?: string;
|
|
374
385
|
literal?: string;
|
|
@@ -377,13 +388,15 @@ describe("TypeResolver", () => {
|
|
|
377
388
|
return {
|
|
378
389
|
IDENTIFIER: () =>
|
|
379
390
|
opts.identifier ? { getText: () => opts.identifier } : null,
|
|
391
|
+
GLOBAL: () => null,
|
|
392
|
+
THIS: () => null,
|
|
380
393
|
literal: () => (opts.literal ? { getText: () => opts.literal } : null),
|
|
381
394
|
expression: () => null,
|
|
382
395
|
castExpression: () =>
|
|
383
396
|
opts.castType
|
|
384
397
|
? { type: () => ({ getText: () => opts.castType }) }
|
|
385
398
|
: null,
|
|
386
|
-
} as Parameters<typeof
|
|
399
|
+
} as Parameters<typeof TypeResolver.getPrimaryExpressionType>[0];
|
|
387
400
|
};
|
|
388
401
|
|
|
389
402
|
it("should return type for identifier in registry", () => {
|
|
@@ -394,32 +407,32 @@ describe("TypeResolver", () => {
|
|
|
394
407
|
isConst: false,
|
|
395
408
|
});
|
|
396
409
|
const ctx = mockPrimary({ identifier: "myVar" });
|
|
397
|
-
expect(
|
|
410
|
+
expect(TypeResolver.getPrimaryExpressionType(ctx)).toBe("u32");
|
|
398
411
|
});
|
|
399
412
|
|
|
400
413
|
it("should return null for identifier not in registry", () => {
|
|
401
414
|
const ctx = mockPrimary({ identifier: "unknownVar" });
|
|
402
|
-
expect(
|
|
415
|
+
expect(TypeResolver.getPrimaryExpressionType(ctx)).toBeNull();
|
|
403
416
|
});
|
|
404
417
|
|
|
405
418
|
it("should return type from literal suffix", () => {
|
|
406
419
|
const ctx = mockPrimary({ literal: "42u8" });
|
|
407
|
-
expect(
|
|
420
|
+
expect(TypeResolver.getPrimaryExpressionType(ctx)).toBe("u8");
|
|
408
421
|
});
|
|
409
422
|
|
|
410
423
|
it("should return bool for boolean literal", () => {
|
|
411
424
|
const ctx = mockPrimary({ literal: "true" });
|
|
412
|
-
expect(
|
|
425
|
+
expect(TypeResolver.getPrimaryExpressionType(ctx)).toBe("bool");
|
|
413
426
|
});
|
|
414
427
|
|
|
415
428
|
it("should return type from cast expression", () => {
|
|
416
429
|
const ctx = mockPrimary({ castType: "i16" });
|
|
417
|
-
expect(
|
|
430
|
+
expect(TypeResolver.getPrimaryExpressionType(ctx)).toBe("i16");
|
|
418
431
|
});
|
|
419
432
|
|
|
420
433
|
it("should return null when no matching component", () => {
|
|
421
434
|
const ctx = mockPrimary({});
|
|
422
|
-
expect(
|
|
435
|
+
expect(TypeResolver.getPrimaryExpressionType(ctx)).toBeNull();
|
|
423
436
|
});
|
|
424
437
|
});
|
|
425
438
|
|
|
@@ -428,7 +441,6 @@ describe("TypeResolver", () => {
|
|
|
428
441
|
// ========================================================================
|
|
429
442
|
|
|
430
443
|
describe("getExpressionType", () => {
|
|
431
|
-
// Helper to create mock expression context with nested structure
|
|
432
444
|
const mockExpressionWithOr = (orCount: number) => {
|
|
433
445
|
const orExprs = Array(orCount)
|
|
434
446
|
.fill(null)
|
|
@@ -450,7 +462,7 @@ describe("TypeResolver", () => {
|
|
|
450
462
|
ternaryExpression: () => ({
|
|
451
463
|
orExpression: () => orExprs,
|
|
452
464
|
}),
|
|
453
|
-
} as unknown as Parameters<typeof
|
|
465
|
+
} as unknown as Parameters<typeof TypeResolver.getExpressionType>[0];
|
|
454
466
|
};
|
|
455
467
|
|
|
456
468
|
const mockExpressionWithAnd = (andCount: number) => {
|
|
@@ -466,7 +478,7 @@ describe("TypeResolver", () => {
|
|
|
466
478
|
ternaryExpression: () => ({
|
|
467
479
|
orExpression: () => [{ andExpression: () => andExprs }],
|
|
468
480
|
}),
|
|
469
|
-
} as unknown as Parameters<typeof
|
|
481
|
+
} as unknown as Parameters<typeof TypeResolver.getExpressionType>[0];
|
|
470
482
|
};
|
|
471
483
|
|
|
472
484
|
const mockExpressionWithEquality = (eqCount: number) => {
|
|
@@ -482,7 +494,7 @@ describe("TypeResolver", () => {
|
|
|
482
494
|
{ andExpression: () => [{ equalityExpression: () => eqExprs }] },
|
|
483
495
|
],
|
|
484
496
|
}),
|
|
485
|
-
} as unknown as Parameters<typeof
|
|
497
|
+
} as unknown as Parameters<typeof TypeResolver.getExpressionType>[0];
|
|
486
498
|
};
|
|
487
499
|
|
|
488
500
|
const mockExpressionWithRelational = (relCount: number) => {
|
|
@@ -506,32 +518,30 @@ describe("TypeResolver", () => {
|
|
|
506
518
|
},
|
|
507
519
|
],
|
|
508
520
|
}),
|
|
509
|
-
} as unknown as Parameters<typeof
|
|
521
|
+
} as unknown as Parameters<typeof TypeResolver.getExpressionType>[0];
|
|
510
522
|
};
|
|
511
523
|
|
|
512
524
|
it("should return null for ternary expression (multiple or expressions)", () => {
|
|
513
525
|
const ctx = mockExpressionWithOr(3);
|
|
514
|
-
expect(
|
|
526
|
+
expect(TypeResolver.getExpressionType(ctx)).toBeNull();
|
|
515
527
|
});
|
|
516
528
|
|
|
517
529
|
it("should return bool for logical OR expression", () => {
|
|
518
530
|
const ctx = mockExpressionWithAnd(2);
|
|
519
|
-
expect(
|
|
531
|
+
expect(TypeResolver.getExpressionType(ctx)).toBe("bool");
|
|
520
532
|
});
|
|
521
533
|
|
|
522
534
|
it("should return bool for logical AND expression", () => {
|
|
523
535
|
const ctx = mockExpressionWithEquality(2);
|
|
524
|
-
expect(
|
|
536
|
+
expect(TypeResolver.getExpressionType(ctx)).toBe("bool");
|
|
525
537
|
});
|
|
526
538
|
|
|
527
539
|
it("should return bool for equality expression", () => {
|
|
528
540
|
const ctx = mockExpressionWithRelational(2);
|
|
529
|
-
expect(
|
|
541
|
+
expect(TypeResolver.getExpressionType(ctx)).toBe("bool");
|
|
530
542
|
});
|
|
531
543
|
|
|
532
544
|
it("should return null for simple arithmetic expression", () => {
|
|
533
|
-
// Expression with 1 of each - falls through to null (can't determine arithmetic type)
|
|
534
|
-
// Need to mock the full depth of the expression tree
|
|
535
545
|
const ctx = {
|
|
536
546
|
ternaryExpression: () => ({
|
|
537
547
|
orExpression: () => [
|
|
@@ -554,7 +564,6 @@ describe("TypeResolver", () => {
|
|
|
554
564
|
{
|
|
555
565
|
multiplicativeExpression: () => [
|
|
556
566
|
{
|
|
557
|
-
// 2 unary expressions = binary operation, can't determine type
|
|
558
567
|
unaryExpression: () => [
|
|
559
568
|
{},
|
|
560
569
|
{},
|
|
@@ -580,9 +589,9 @@ describe("TypeResolver", () => {
|
|
|
580
589
|
},
|
|
581
590
|
],
|
|
582
591
|
}),
|
|
583
|
-
} as unknown as Parameters<typeof
|
|
592
|
+
} as unknown as Parameters<typeof TypeResolver.getExpressionType>[0];
|
|
584
593
|
|
|
585
|
-
expect(
|
|
594
|
+
expect(TypeResolver.getExpressionType(ctx)).toBeNull();
|
|
586
595
|
});
|
|
587
596
|
});
|
|
588
597
|
|
|
@@ -595,9 +604,11 @@ describe("TypeResolver", () => {
|
|
|
595
604
|
const ctx = {
|
|
596
605
|
primaryExpression: () => null,
|
|
597
606
|
children: [],
|
|
598
|
-
} as unknown as Parameters<
|
|
607
|
+
} as unknown as Parameters<
|
|
608
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
609
|
+
>[0];
|
|
599
610
|
|
|
600
|
-
expect(
|
|
611
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBeNull();
|
|
601
612
|
});
|
|
602
613
|
|
|
603
614
|
it("should return type from simple identifier", () => {
|
|
@@ -616,9 +627,11 @@ describe("TypeResolver", () => {
|
|
|
616
627
|
castExpression: () => null,
|
|
617
628
|
}),
|
|
618
629
|
children: [{ getText: () => "counter" }],
|
|
619
|
-
} as unknown as Parameters<
|
|
630
|
+
} as unknown as Parameters<
|
|
631
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
632
|
+
>[0];
|
|
620
633
|
|
|
621
|
-
expect(
|
|
634
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("u32");
|
|
622
635
|
});
|
|
623
636
|
|
|
624
637
|
it("should return null when primary type cannot be determined", () => {
|
|
@@ -630,9 +643,11 @@ describe("TypeResolver", () => {
|
|
|
630
643
|
castExpression: () => null,
|
|
631
644
|
}),
|
|
632
645
|
children: [{ getText: () => "unknownVar" }],
|
|
633
|
-
} as unknown as Parameters<
|
|
646
|
+
} as unknown as Parameters<
|
|
647
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
648
|
+
>[0];
|
|
634
649
|
|
|
635
|
-
expect(
|
|
650
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBeNull();
|
|
636
651
|
});
|
|
637
652
|
|
|
638
653
|
it("should return member type for struct member access", () => {
|
|
@@ -652,9 +667,11 @@ describe("TypeResolver", () => {
|
|
|
652
667
|
castExpression: () => null,
|
|
653
668
|
}),
|
|
654
669
|
children: [{ getText: () => "point" }, { getText: () => ".x" }],
|
|
655
|
-
} as unknown as Parameters<
|
|
670
|
+
} as unknown as Parameters<
|
|
671
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
672
|
+
>[0];
|
|
656
673
|
|
|
657
|
-
expect(
|
|
674
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("i32");
|
|
658
675
|
});
|
|
659
676
|
|
|
660
677
|
it("should return null for unknown member", () => {
|
|
@@ -674,9 +691,11 @@ describe("TypeResolver", () => {
|
|
|
674
691
|
castExpression: () => null,
|
|
675
692
|
}),
|
|
676
693
|
children: [{ getText: () => "point" }, { getText: () => ".unknown" }],
|
|
677
|
-
} as unknown as Parameters<
|
|
694
|
+
} as unknown as Parameters<
|
|
695
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
696
|
+
>[0];
|
|
678
697
|
|
|
679
|
-
expect(
|
|
698
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBeNull();
|
|
680
699
|
});
|
|
681
700
|
|
|
682
701
|
it("should return null for range bit indexing", () => {
|
|
@@ -695,9 +714,11 @@ describe("TypeResolver", () => {
|
|
|
695
714
|
castExpression: () => null,
|
|
696
715
|
}),
|
|
697
716
|
children: [{ getText: () => "flags" }, { getText: () => "[0, 8]" }],
|
|
698
|
-
} as unknown as Parameters<
|
|
717
|
+
} as unknown as Parameters<
|
|
718
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
719
|
+
>[0];
|
|
699
720
|
|
|
700
|
-
expect(
|
|
721
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBeNull();
|
|
701
722
|
});
|
|
702
723
|
|
|
703
724
|
it("should return bool for single bit indexing on integer", () => {
|
|
@@ -716,12 +737,14 @@ describe("TypeResolver", () => {
|
|
|
716
737
|
castExpression: () => null,
|
|
717
738
|
}),
|
|
718
739
|
children: [{ getText: () => "flags" }, { getText: () => "[7]" }],
|
|
719
|
-
} as unknown as Parameters<
|
|
740
|
+
} as unknown as Parameters<
|
|
741
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
742
|
+
>[0];
|
|
720
743
|
|
|
721
|
-
expect(
|
|
744
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("bool");
|
|
722
745
|
});
|
|
723
746
|
|
|
724
|
-
it("should return element type for array indexing
|
|
747
|
+
it("should return element type for struct array member indexing", () => {
|
|
725
748
|
typeRegistry.set("data", {
|
|
726
749
|
baseType: "Data",
|
|
727
750
|
bitWidth: 0,
|
|
@@ -742,11 +765,219 @@ describe("TypeResolver", () => {
|
|
|
742
765
|
{ getText: () => ".values" },
|
|
743
766
|
{ getText: () => "[0]" },
|
|
744
767
|
],
|
|
745
|
-
} as unknown as Parameters<
|
|
768
|
+
} as unknown as Parameters<
|
|
769
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
770
|
+
>[0];
|
|
771
|
+
|
|
772
|
+
// Bug fix: After .values (u8 array), [0] should be array element access -> "u8"
|
|
773
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("u8");
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it("should return element type for direct array variable indexing", () => {
|
|
777
|
+
typeRegistry.set("arr", {
|
|
778
|
+
baseType: "u8",
|
|
779
|
+
bitWidth: 8,
|
|
780
|
+
isArray: true,
|
|
781
|
+
isConst: false,
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
const ctx = {
|
|
785
|
+
primaryExpression: () => ({
|
|
786
|
+
IDENTIFIER: () => ({ getText: () => "arr" }),
|
|
787
|
+
literal: () => null,
|
|
788
|
+
expression: () => null,
|
|
789
|
+
castExpression: () => null,
|
|
790
|
+
}),
|
|
791
|
+
children: [{ getText: () => "arr" }, { getText: () => "[0]" }],
|
|
792
|
+
} as unknown as Parameters<
|
|
793
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
794
|
+
>[0];
|
|
795
|
+
|
|
796
|
+
// u8 array with [0] should be array element access -> "u8" (not "bool")
|
|
797
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("u8");
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it("should return bool for bit indexing on plain integer variable", () => {
|
|
801
|
+
typeRegistry.set("val", {
|
|
802
|
+
baseType: "u8",
|
|
803
|
+
bitWidth: 8,
|
|
804
|
+
isArray: false,
|
|
805
|
+
isConst: false,
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const ctx = {
|
|
809
|
+
primaryExpression: () => ({
|
|
810
|
+
IDENTIFIER: () => ({ getText: () => "val" }),
|
|
811
|
+
literal: () => null,
|
|
812
|
+
expression: () => null,
|
|
813
|
+
castExpression: () => null,
|
|
814
|
+
}),
|
|
815
|
+
children: [{ getText: () => "val" }, { getText: () => "[0]" }],
|
|
816
|
+
} as unknown as Parameters<
|
|
817
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
818
|
+
>[0];
|
|
819
|
+
|
|
820
|
+
// Plain u8 (not array) with [0] should be bit indexing -> "bool"
|
|
821
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("bool");
|
|
822
|
+
});
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
// ========================================================================
|
|
826
|
+
// global/this Primary Expression Handling
|
|
827
|
+
// ========================================================================
|
|
828
|
+
|
|
829
|
+
describe("getPostfixExpressionType with global/this", () => {
|
|
830
|
+
it("should resolve global.structVar.field type", () => {
|
|
831
|
+
typeRegistry.set("config", {
|
|
832
|
+
baseType: "TConfig",
|
|
833
|
+
bitWidth: 0,
|
|
834
|
+
isArray: false,
|
|
835
|
+
isConst: false,
|
|
836
|
+
});
|
|
837
|
+
symbolTable.addStructField("TConfig", "value", "u32");
|
|
838
|
+
|
|
839
|
+
const ctx = {
|
|
840
|
+
primaryExpression: () => ({
|
|
841
|
+
IDENTIFIER: () => null,
|
|
842
|
+
GLOBAL: () => ({ getText: () => "global" }),
|
|
843
|
+
THIS: () => null,
|
|
844
|
+
literal: () => null,
|
|
845
|
+
expression: () => null,
|
|
846
|
+
castExpression: () => null,
|
|
847
|
+
}),
|
|
848
|
+
children: [
|
|
849
|
+
{ getText: () => "global" },
|
|
850
|
+
{ getText: () => ".config" },
|
|
851
|
+
{ getText: () => ".value" },
|
|
852
|
+
],
|
|
853
|
+
} as unknown as Parameters<
|
|
854
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
855
|
+
>[0];
|
|
856
|
+
|
|
857
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("u32");
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
it("should resolve global.arrayVar[0] element type", () => {
|
|
861
|
+
typeRegistry.set("inputs", {
|
|
862
|
+
baseType: "TInput",
|
|
863
|
+
bitWidth: 0,
|
|
864
|
+
isArray: true,
|
|
865
|
+
isConst: false,
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
const ctx = {
|
|
869
|
+
primaryExpression: () => ({
|
|
870
|
+
IDENTIFIER: () => null,
|
|
871
|
+
GLOBAL: () => ({ getText: () => "global" }),
|
|
872
|
+
THIS: () => null,
|
|
873
|
+
literal: () => null,
|
|
874
|
+
expression: () => null,
|
|
875
|
+
castExpression: () => null,
|
|
876
|
+
}),
|
|
877
|
+
children: [
|
|
878
|
+
{ getText: () => "global" },
|
|
879
|
+
{ getText: () => ".inputs" },
|
|
880
|
+
{ getText: () => "[0]" },
|
|
881
|
+
],
|
|
882
|
+
} as unknown as Parameters<
|
|
883
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
884
|
+
>[0];
|
|
746
885
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
886
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("TInput");
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
it("should resolve this.scopeVar type", () => {
|
|
890
|
+
CodeGenState.currentScope = "Motor";
|
|
891
|
+
typeRegistry.set("Motor_speed", {
|
|
892
|
+
baseType: "u32",
|
|
893
|
+
bitWidth: 32,
|
|
894
|
+
isArray: false,
|
|
895
|
+
isConst: false,
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
const ctx = {
|
|
899
|
+
primaryExpression: () => ({
|
|
900
|
+
IDENTIFIER: () => null,
|
|
901
|
+
GLOBAL: () => null,
|
|
902
|
+
THIS: () => ({ getText: () => "this" }),
|
|
903
|
+
literal: () => null,
|
|
904
|
+
expression: () => null,
|
|
905
|
+
castExpression: () => null,
|
|
906
|
+
}),
|
|
907
|
+
children: [{ getText: () => "this" }, { getText: () => ".speed" }],
|
|
908
|
+
} as unknown as Parameters<
|
|
909
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
910
|
+
>[0];
|
|
911
|
+
|
|
912
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("u32");
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
it("should return null for global.unknownVar", () => {
|
|
916
|
+
const ctx = {
|
|
917
|
+
primaryExpression: () => ({
|
|
918
|
+
IDENTIFIER: () => null,
|
|
919
|
+
GLOBAL: () => ({ getText: () => "global" }),
|
|
920
|
+
THIS: () => null,
|
|
921
|
+
literal: () => null,
|
|
922
|
+
expression: () => null,
|
|
923
|
+
castExpression: () => null,
|
|
924
|
+
}),
|
|
925
|
+
children: [{ getText: () => "global" }, { getText: () => ".unknown" }],
|
|
926
|
+
} as unknown as Parameters<
|
|
927
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
928
|
+
>[0];
|
|
929
|
+
|
|
930
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBeNull();
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
it("should return null for this.X without current scope", () => {
|
|
934
|
+
CodeGenState.currentScope = null;
|
|
935
|
+
|
|
936
|
+
const ctx = {
|
|
937
|
+
primaryExpression: () => ({
|
|
938
|
+
IDENTIFIER: () => null,
|
|
939
|
+
GLOBAL: () => null,
|
|
940
|
+
THIS: () => ({ getText: () => "this" }),
|
|
941
|
+
literal: () => null,
|
|
942
|
+
expression: () => null,
|
|
943
|
+
castExpression: () => null,
|
|
944
|
+
}),
|
|
945
|
+
children: [{ getText: () => "this" }, { getText: () => ".speed" }],
|
|
946
|
+
} as unknown as Parameters<
|
|
947
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
948
|
+
>[0];
|
|
949
|
+
|
|
950
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBeNull();
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it("should resolve global.struct.enumField for enum type", () => {
|
|
954
|
+
typeRegistry.set("input", {
|
|
955
|
+
baseType: "TInput",
|
|
956
|
+
bitWidth: 0,
|
|
957
|
+
isArray: false,
|
|
958
|
+
isConst: false,
|
|
959
|
+
});
|
|
960
|
+
symbolTable.addStructField("TInput", "assignedValue", "EValueId");
|
|
961
|
+
|
|
962
|
+
const ctx = {
|
|
963
|
+
primaryExpression: () => ({
|
|
964
|
+
IDENTIFIER: () => null,
|
|
965
|
+
GLOBAL: () => ({ getText: () => "global" }),
|
|
966
|
+
THIS: () => null,
|
|
967
|
+
literal: () => null,
|
|
968
|
+
expression: () => null,
|
|
969
|
+
castExpression: () => null,
|
|
970
|
+
}),
|
|
971
|
+
children: [
|
|
972
|
+
{ getText: () => "global" },
|
|
973
|
+
{ getText: () => ".input" },
|
|
974
|
+
{ getText: () => ".assignedValue" },
|
|
975
|
+
],
|
|
976
|
+
} as unknown as Parameters<
|
|
977
|
+
typeof TypeResolver.getPostfixExpressionType
|
|
978
|
+
>[0];
|
|
979
|
+
|
|
980
|
+
expect(TypeResolver.getPostfixExpressionType(ctx)).toBe("EValueId");
|
|
750
981
|
});
|
|
751
982
|
});
|
|
752
983
|
|
|
@@ -774,18 +1005,18 @@ describe("TypeResolver", () => {
|
|
|
774
1005
|
children: [{ getText: () => "value" }],
|
|
775
1006
|
}),
|
|
776
1007
|
unaryExpression: () => null,
|
|
777
|
-
} as unknown as Parameters<typeof
|
|
1008
|
+
} as unknown as Parameters<typeof TypeResolver.getUnaryExpressionType>[0];
|
|
778
1009
|
|
|
779
|
-
expect(
|
|
1010
|
+
expect(TypeResolver.getUnaryExpressionType(ctx)).toBe("i32");
|
|
780
1011
|
});
|
|
781
1012
|
|
|
782
1013
|
it("should return null when no postfix or unary", () => {
|
|
783
1014
|
const ctx = {
|
|
784
1015
|
postfixExpression: () => null,
|
|
785
1016
|
unaryExpression: () => null,
|
|
786
|
-
} as unknown as Parameters<typeof
|
|
1017
|
+
} as unknown as Parameters<typeof TypeResolver.getUnaryExpressionType>[0];
|
|
787
1018
|
|
|
788
|
-
expect(
|
|
1019
|
+
expect(TypeResolver.getUnaryExpressionType(ctx)).toBeNull();
|
|
789
1020
|
});
|
|
790
1021
|
|
|
791
1022
|
it("should recurse through unary expression chain", () => {
|
|
@@ -796,7 +1027,6 @@ describe("TypeResolver", () => {
|
|
|
796
1027
|
isConst: false,
|
|
797
1028
|
});
|
|
798
1029
|
|
|
799
|
-
// Represents: -x (unary minus with nested expression)
|
|
800
1030
|
const ctx = {
|
|
801
1031
|
postfixExpression: () => null,
|
|
802
1032
|
unaryExpression: () => ({
|
|
@@ -811,9 +1041,9 @@ describe("TypeResolver", () => {
|
|
|
811
1041
|
}),
|
|
812
1042
|
unaryExpression: () => null,
|
|
813
1043
|
}),
|
|
814
|
-
} as unknown as Parameters<typeof
|
|
1044
|
+
} as unknown as Parameters<typeof TypeResolver.getUnaryExpressionType>[0];
|
|
815
1045
|
|
|
816
|
-
expect(
|
|
1046
|
+
expect(TypeResolver.getUnaryExpressionType(ctx)).toBe("i32");
|
|
817
1047
|
});
|
|
818
1048
|
});
|
|
819
1049
|
|
|
@@ -822,57 +1052,60 @@ describe("TypeResolver", () => {
|
|
|
822
1052
|
// ========================================================================
|
|
823
1053
|
|
|
824
1054
|
describe("getLiteralType", () => {
|
|
825
|
-
// Helper to create mock literal context
|
|
826
1055
|
const mockLiteral = (text: string) =>
|
|
827
1056
|
({ getText: () => text }) as Parameters<
|
|
828
|
-
typeof
|
|
1057
|
+
typeof TypeResolver.getLiteralType
|
|
829
1058
|
>[0];
|
|
830
1059
|
|
|
831
1060
|
describe("boolean literals", () => {
|
|
832
1061
|
it("should return bool for true", () => {
|
|
833
|
-
expect(
|
|
1062
|
+
expect(TypeResolver.getLiteralType(mockLiteral("true"))).toBe("bool");
|
|
834
1063
|
});
|
|
835
1064
|
|
|
836
1065
|
it("should return bool for false", () => {
|
|
837
|
-
expect(
|
|
1066
|
+
expect(TypeResolver.getLiteralType(mockLiteral("false"))).toBe("bool");
|
|
838
1067
|
});
|
|
839
1068
|
});
|
|
840
1069
|
|
|
841
1070
|
describe("integer suffixes", () => {
|
|
842
1071
|
it("should detect u8 suffix", () => {
|
|
843
|
-
expect(
|
|
844
|
-
expect(
|
|
1072
|
+
expect(TypeResolver.getLiteralType(mockLiteral("255u8"))).toBe("u8");
|
|
1073
|
+
expect(TypeResolver.getLiteralType(mockLiteral("0U8"))).toBe("u8");
|
|
845
1074
|
});
|
|
846
1075
|
|
|
847
1076
|
it("should detect u16 suffix", () => {
|
|
848
|
-
expect(
|
|
1077
|
+
expect(TypeResolver.getLiteralType(mockLiteral("1000u16"))).toBe("u16");
|
|
849
1078
|
});
|
|
850
1079
|
|
|
851
1080
|
it("should detect u32 suffix", () => {
|
|
852
|
-
expect(
|
|
1081
|
+
expect(TypeResolver.getLiteralType(mockLiteral("1000000u32"))).toBe(
|
|
1082
|
+
"u32",
|
|
1083
|
+
);
|
|
853
1084
|
});
|
|
854
1085
|
|
|
855
1086
|
it("should detect u64 suffix", () => {
|
|
856
|
-
expect(
|
|
1087
|
+
expect(TypeResolver.getLiteralType(mockLiteral("1000000000u64"))).toBe(
|
|
857
1088
|
"u64",
|
|
858
1089
|
);
|
|
859
1090
|
});
|
|
860
1091
|
|
|
861
1092
|
it("should detect i8 suffix", () => {
|
|
862
|
-
expect(
|
|
863
|
-
expect(
|
|
1093
|
+
expect(TypeResolver.getLiteralType(mockLiteral("-50i8"))).toBe("i8");
|
|
1094
|
+
expect(TypeResolver.getLiteralType(mockLiteral("50I8"))).toBe("i8");
|
|
864
1095
|
});
|
|
865
1096
|
|
|
866
1097
|
it("should detect i16 suffix", () => {
|
|
867
|
-
expect(
|
|
1098
|
+
expect(TypeResolver.getLiteralType(mockLiteral("1000i16"))).toBe("i16");
|
|
868
1099
|
});
|
|
869
1100
|
|
|
870
1101
|
it("should detect i32 suffix", () => {
|
|
871
|
-
expect(
|
|
1102
|
+
expect(TypeResolver.getLiteralType(mockLiteral("1000000i32"))).toBe(
|
|
1103
|
+
"i32",
|
|
1104
|
+
);
|
|
872
1105
|
});
|
|
873
1106
|
|
|
874
1107
|
it("should detect i64 suffix", () => {
|
|
875
|
-
expect(
|
|
1108
|
+
expect(TypeResolver.getLiteralType(mockLiteral("1000000000i64"))).toBe(
|
|
876
1109
|
"i64",
|
|
877
1110
|
);
|
|
878
1111
|
});
|
|
@@ -880,27 +1113,31 @@ describe("TypeResolver", () => {
|
|
|
880
1113
|
|
|
881
1114
|
describe("float suffixes", () => {
|
|
882
1115
|
it("should detect f32 suffix", () => {
|
|
883
|
-
expect(
|
|
884
|
-
expect(
|
|
1116
|
+
expect(TypeResolver.getLiteralType(mockLiteral("3.14f32"))).toBe("f32");
|
|
1117
|
+
expect(TypeResolver.getLiteralType(mockLiteral("3.14F32"))).toBe("f32");
|
|
885
1118
|
});
|
|
886
1119
|
|
|
887
1120
|
it("should detect f64 suffix", () => {
|
|
888
|
-
expect(
|
|
889
|
-
|
|
1121
|
+
expect(TypeResolver.getLiteralType(mockLiteral("3.14159f64"))).toBe(
|
|
1122
|
+
"f64",
|
|
1123
|
+
);
|
|
1124
|
+
expect(TypeResolver.getLiteralType(mockLiteral("3.14159F64"))).toBe(
|
|
1125
|
+
"f64",
|
|
1126
|
+
);
|
|
890
1127
|
});
|
|
891
1128
|
});
|
|
892
1129
|
|
|
893
1130
|
describe("unsuffixed literals", () => {
|
|
894
1131
|
it("should return null for unsuffixed integer", () => {
|
|
895
|
-
expect(
|
|
1132
|
+
expect(TypeResolver.getLiteralType(mockLiteral("42"))).toBeNull();
|
|
896
1133
|
});
|
|
897
1134
|
|
|
898
1135
|
it("should return null for unsuffixed hex", () => {
|
|
899
|
-
expect(
|
|
1136
|
+
expect(TypeResolver.getLiteralType(mockLiteral("0xFF"))).toBeNull();
|
|
900
1137
|
});
|
|
901
1138
|
|
|
902
1139
|
it("should return null for unsuffixed float", () => {
|
|
903
|
-
expect(
|
|
1140
|
+
expect(TypeResolver.getLiteralType(mockLiteral("3.14"))).toBeNull();
|
|
904
1141
|
});
|
|
905
1142
|
});
|
|
906
1143
|
});
|
|
@@ -914,7 +1151,7 @@ describe("TypeResolver", () => {
|
|
|
914
1151
|
symbolTable.addStructField("Point", "x", "i32");
|
|
915
1152
|
symbolTable.addStructField("Point", "y", "i32");
|
|
916
1153
|
|
|
917
|
-
const xInfo =
|
|
1154
|
+
const xInfo = TypeResolver.getMemberTypeInfo("Point", "x");
|
|
918
1155
|
expect(xInfo).toBeDefined();
|
|
919
1156
|
expect(xInfo?.baseType).toBe("i32");
|
|
920
1157
|
expect(xInfo?.isArray).toBe(false);
|
|
@@ -923,19 +1160,21 @@ describe("TypeResolver", () => {
|
|
|
923
1160
|
it("should return array info for array fields", () => {
|
|
924
1161
|
symbolTable.addStructField("Buffer", "data", "u8", [256]);
|
|
925
1162
|
|
|
926
|
-
const dataInfo =
|
|
1163
|
+
const dataInfo = TypeResolver.getMemberTypeInfo("Buffer", "data");
|
|
927
1164
|
expect(dataInfo).toBeDefined();
|
|
928
1165
|
expect(dataInfo?.baseType).toBe("u8");
|
|
929
1166
|
expect(dataInfo?.isArray).toBe(true);
|
|
930
1167
|
});
|
|
931
1168
|
|
|
932
1169
|
it("should return undefined for unknown struct", () => {
|
|
933
|
-
expect(
|
|
1170
|
+
expect(
|
|
1171
|
+
TypeResolver.getMemberTypeInfo("Unknown", "field"),
|
|
1172
|
+
).toBeUndefined();
|
|
934
1173
|
});
|
|
935
1174
|
|
|
936
1175
|
it("should return undefined for unknown field", () => {
|
|
937
1176
|
symbolTable.addStructField("Point", "x", "i32");
|
|
938
|
-
expect(
|
|
1177
|
+
expect(TypeResolver.getMemberTypeInfo("Point", "z")).toBeUndefined();
|
|
939
1178
|
});
|
|
940
1179
|
});
|
|
941
1180
|
});
|