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
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for EnumTypeResolver - enum type inference from expressions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
6
|
+
import EnumTypeResolver from "../EnumTypeResolver";
|
|
7
|
+
import CodeGenState from "../../CodeGenState";
|
|
8
|
+
import SymbolTable from "../../../../logic/symbols/SymbolTable";
|
|
9
|
+
import ICodeGenSymbols from "../../../../types/ICodeGenSymbols";
|
|
10
|
+
|
|
11
|
+
describe("EnumTypeResolver", () => {
|
|
12
|
+
const createMockSymbols = (
|
|
13
|
+
overrides: Partial<ICodeGenSymbols> = {},
|
|
14
|
+
): ICodeGenSymbols =>
|
|
15
|
+
({
|
|
16
|
+
knownScopes: new Set(),
|
|
17
|
+
knownEnums: new Set(),
|
|
18
|
+
knownBitmaps: new Set(),
|
|
19
|
+
knownStructs: new Set(),
|
|
20
|
+
knownRegisters: new Set(),
|
|
21
|
+
bitmapBitWidth: new Map(),
|
|
22
|
+
bitmapFields: new Map(),
|
|
23
|
+
bitmapBackingType: new Map(),
|
|
24
|
+
enumMembers: new Map(),
|
|
25
|
+
structFields: new Map(),
|
|
26
|
+
structFieldArrays: new Map(),
|
|
27
|
+
structFieldDimensions: new Map(),
|
|
28
|
+
functionReturnTypes: new Map(),
|
|
29
|
+
scopeMembers: new Map(),
|
|
30
|
+
scopeMemberVisibility: new Map(),
|
|
31
|
+
scopedRegisters: new Map(),
|
|
32
|
+
registerMemberAccess: new Map(),
|
|
33
|
+
registerMemberTypes: new Map(),
|
|
34
|
+
registerBaseAddresses: new Map(),
|
|
35
|
+
registerMemberOffsets: new Map(),
|
|
36
|
+
registerMemberCTypes: new Map(),
|
|
37
|
+
scopeVariableUsage: new Map(),
|
|
38
|
+
scopePrivateConstValues: new Map(),
|
|
39
|
+
getSingleFunctionForVariable: () => null,
|
|
40
|
+
hasPublicSymbols: () => false,
|
|
41
|
+
...overrides,
|
|
42
|
+
}) as ICodeGenSymbols;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
CodeGenState.reset();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("resolve() - function call patterns", () => {
|
|
49
|
+
it("resolves function call returning enum type", () => {
|
|
50
|
+
CodeGenState.symbols = createMockSymbols({
|
|
51
|
+
knownEnums: new Set(["State"]),
|
|
52
|
+
functionReturnTypes: new Map([["getState", "State"]]),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const mockCtx = { getText: () => "getState()" };
|
|
56
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("resolves this.method() returning enum type", () => {
|
|
60
|
+
CodeGenState.currentScope = "Motor";
|
|
61
|
+
CodeGenState.symbols = createMockSymbols({
|
|
62
|
+
knownEnums: new Set(["State"]),
|
|
63
|
+
functionReturnTypes: new Map([["Motor_getState", "State"]]),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const mockCtx = { getText: () => "this.getState()" };
|
|
67
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("resolves global.func() returning enum type", () => {
|
|
71
|
+
CodeGenState.symbols = createMockSymbols({
|
|
72
|
+
knownEnums: new Set(["State"]),
|
|
73
|
+
functionReturnTypes: new Map([["getGlobalState", "State"]]),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const mockCtx = { getText: () => "global.getGlobalState()" };
|
|
77
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("resolves Scope.method() returning enum type", () => {
|
|
81
|
+
CodeGenState.symbols = createMockSymbols({
|
|
82
|
+
knownScopes: new Set(["Motor"]),
|
|
83
|
+
knownEnums: new Set(["State"]),
|
|
84
|
+
functionReturnTypes: new Map([["Motor_getState", "State"]]),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const mockCtx = { getText: () => "Motor.getState()" };
|
|
88
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("resolves global.Scope.method() returning enum type", () => {
|
|
92
|
+
CodeGenState.symbols = createMockSymbols({
|
|
93
|
+
knownScopes: new Set(["Motor"]),
|
|
94
|
+
knownEnums: new Set(["State"]),
|
|
95
|
+
functionReturnTypes: new Map([["Motor_getState", "State"]]),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const mockCtx = { getText: () => "global.Motor.getState()" };
|
|
99
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("returns null for function returning non-enum type", () => {
|
|
103
|
+
CodeGenState.symbols = createMockSymbols({
|
|
104
|
+
functionReturnTypes: new Map([["getValue", "u32"]]),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const mockCtx = { getText: () => "getValue()" };
|
|
108
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("returns null for unknown function", () => {
|
|
112
|
+
CodeGenState.symbols = createMockSymbols();
|
|
113
|
+
|
|
114
|
+
const mockCtx = { getText: () => "unknownFunc()" };
|
|
115
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("resolve() - simple identifier patterns", () => {
|
|
120
|
+
it("resolves enum variable by type registry lookup", () => {
|
|
121
|
+
CodeGenState.symbols = createMockSymbols({
|
|
122
|
+
knownEnums: new Set(["State"]),
|
|
123
|
+
});
|
|
124
|
+
CodeGenState.typeRegistry.set("currentState", {
|
|
125
|
+
baseType: "State",
|
|
126
|
+
bitWidth: 0,
|
|
127
|
+
isArray: false,
|
|
128
|
+
isConst: false,
|
|
129
|
+
isEnum: true,
|
|
130
|
+
enumTypeName: "State",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const mockCtx = { getText: () => "currentState" };
|
|
134
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("returns null for non-enum variable", () => {
|
|
138
|
+
CodeGenState.typeRegistry.set("count", {
|
|
139
|
+
baseType: "u32",
|
|
140
|
+
bitWidth: 32,
|
|
141
|
+
isArray: false,
|
|
142
|
+
isConst: false,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const mockCtx = { getText: () => "count" };
|
|
146
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("resolve() - member access patterns", () => {
|
|
151
|
+
it("resolves simple enum member access: State.IDLE", () => {
|
|
152
|
+
CodeGenState.symbols = createMockSymbols({
|
|
153
|
+
knownEnums: new Set(["State"]),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const mockCtx = { getText: () => "State.IDLE" };
|
|
157
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("resolves scoped enum: Motor.State.IDLE -> Motor_State", () => {
|
|
161
|
+
CodeGenState.symbols = createMockSymbols({
|
|
162
|
+
knownEnums: new Set(["Motor_State"]),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const mockCtx = { getText: () => "Motor.State.IDLE" };
|
|
166
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("resolves this.Enum.MEMBER inside scope", () => {
|
|
170
|
+
CodeGenState.currentScope = "Motor";
|
|
171
|
+
CodeGenState.symbols = createMockSymbols({
|
|
172
|
+
knownEnums: new Set(["Motor_State"]),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const mockCtx = { getText: () => "this.State.IDLE" };
|
|
176
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("resolves global.Enum.MEMBER pattern", () => {
|
|
180
|
+
CodeGenState.symbols = createMockSymbols({
|
|
181
|
+
knownEnums: new Set(["ECategory"]),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const mockCtx = { getText: () => "global.ECategory.CAT_A" };
|
|
185
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("ECategory");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("resolves this.variable pattern for enum-typed scope member", () => {
|
|
189
|
+
CodeGenState.currentScope = "Motor";
|
|
190
|
+
CodeGenState.symbols = createMockSymbols({
|
|
191
|
+
knownEnums: new Set(["Motor_State"]),
|
|
192
|
+
});
|
|
193
|
+
CodeGenState.typeRegistry.set("Motor_current", {
|
|
194
|
+
baseType: "Motor_State",
|
|
195
|
+
bitWidth: 0,
|
|
196
|
+
isArray: false,
|
|
197
|
+
isConst: false,
|
|
198
|
+
isEnum: true,
|
|
199
|
+
enumTypeName: "Motor_State",
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const mockCtx = { getText: () => "this.current" };
|
|
203
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe("resolve() - TypeResolver fallback for struct member chains", () => {
|
|
208
|
+
/**
|
|
209
|
+
* Helper to build a mock ExpressionContext that contains a full postfix
|
|
210
|
+
* expression tree: global.input.assignedValue
|
|
211
|
+
*/
|
|
212
|
+
const buildStructChainCtx = (
|
|
213
|
+
primaryToken: "GLOBAL" | "THIS" | "IDENTIFIER",
|
|
214
|
+
primaryText: string,
|
|
215
|
+
suffixes: string[],
|
|
216
|
+
) => {
|
|
217
|
+
const primary = {
|
|
218
|
+
IDENTIFIER: () =>
|
|
219
|
+
primaryToken === "IDENTIFIER" ? { getText: () => primaryText } : null,
|
|
220
|
+
GLOBAL: () =>
|
|
221
|
+
primaryToken === "GLOBAL" ? { getText: () => "global" } : null,
|
|
222
|
+
THIS: () =>
|
|
223
|
+
primaryToken === "THIS" ? { getText: () => "this" } : null,
|
|
224
|
+
literal: () => null,
|
|
225
|
+
expression: () => null,
|
|
226
|
+
castExpression: () => null,
|
|
227
|
+
};
|
|
228
|
+
const children = [
|
|
229
|
+
{ getText: () => primaryText },
|
|
230
|
+
...suffixes.map((s) => ({ getText: () => s })),
|
|
231
|
+
];
|
|
232
|
+
const postfix = { primaryExpression: () => primary, children };
|
|
233
|
+
|
|
234
|
+
// Build the full expression tree wrapping the postfix
|
|
235
|
+
const unary = {
|
|
236
|
+
postfixExpression: () => postfix,
|
|
237
|
+
unaryExpression: () => null,
|
|
238
|
+
};
|
|
239
|
+
const mult = { unaryExpression: () => [unary] };
|
|
240
|
+
const add = { multiplicativeExpression: () => [mult] };
|
|
241
|
+
const shift = { additiveExpression: () => [add] };
|
|
242
|
+
const bitAnd = { shiftExpression: () => [shift] };
|
|
243
|
+
const bitXor = { bitwiseAndExpression: () => [bitAnd] };
|
|
244
|
+
const bitOr = { bitwiseXorExpression: () => [bitXor] };
|
|
245
|
+
const rel = { bitwiseOrExpression: () => [bitOr] };
|
|
246
|
+
const eq = { relationalExpression: () => [rel] };
|
|
247
|
+
const and = { equalityExpression: () => [eq] };
|
|
248
|
+
const or = { andExpression: () => [and] };
|
|
249
|
+
const ternary = { orExpression: () => [or] };
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
getText: () => primaryText + suffixes.join(""),
|
|
253
|
+
ternaryExpression: () => ternary,
|
|
254
|
+
} as never;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
it("resolves global.struct.enumField via TypeResolver fallback", () => {
|
|
258
|
+
const symbolTable = new SymbolTable();
|
|
259
|
+
symbolTable.addStructField("TInput", "assignedValue", "EValueId");
|
|
260
|
+
CodeGenState.symbolTable = symbolTable;
|
|
261
|
+
CodeGenState.symbols = createMockSymbols({
|
|
262
|
+
knownEnums: new Set(["EValueId"]),
|
|
263
|
+
});
|
|
264
|
+
CodeGenState.typeRegistry.set("input", {
|
|
265
|
+
baseType: "TInput",
|
|
266
|
+
bitWidth: 0,
|
|
267
|
+
isArray: false,
|
|
268
|
+
isConst: false,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const ctx = buildStructChainCtx("GLOBAL", "global", [
|
|
272
|
+
".input",
|
|
273
|
+
".assignedValue",
|
|
274
|
+
]);
|
|
275
|
+
expect(EnumTypeResolver.resolve(ctx)).toBe("EValueId");
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("returns null when struct field is not an enum type", () => {
|
|
279
|
+
const symbolTable = new SymbolTable();
|
|
280
|
+
symbolTable.addStructField("TInput", "count", "u32");
|
|
281
|
+
CodeGenState.symbolTable = symbolTable;
|
|
282
|
+
CodeGenState.symbols = createMockSymbols();
|
|
283
|
+
CodeGenState.typeRegistry.set("input", {
|
|
284
|
+
baseType: "TInput",
|
|
285
|
+
bitWidth: 0,
|
|
286
|
+
isArray: false,
|
|
287
|
+
isConst: false,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const ctx = buildStructChainCtx("GLOBAL", "global", [".input", ".count"]);
|
|
291
|
+
expect(EnumTypeResolver.resolve(ctx)).toBeNull();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("returns null for RelationalExpressionContext (no ternaryExpression)", () => {
|
|
295
|
+
CodeGenState.symbols = createMockSymbols();
|
|
296
|
+
// RelationalExpressionContext doesn't have ternaryExpression
|
|
297
|
+
const ctx = { getText: () => "something.weird" } as never;
|
|
298
|
+
expect(EnumTypeResolver.resolve(ctx)).toBeNull();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("resolve() - edge cases", () => {
|
|
303
|
+
it("returns null for this.Enum.MEMBER when not in a scope", () => {
|
|
304
|
+
CodeGenState.currentScope = null;
|
|
305
|
+
CodeGenState.symbols = createMockSymbols({
|
|
306
|
+
knownEnums: new Set(["Motor_State"]),
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const mockCtx = { getText: () => "this.State.IDLE" };
|
|
310
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("returns null for this.variable when not in a scope", () => {
|
|
314
|
+
CodeGenState.currentScope = null;
|
|
315
|
+
|
|
316
|
+
const mockCtx = { getText: () => "this.current" };
|
|
317
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("returns null for unknown enum in scoped pattern", () => {
|
|
321
|
+
CodeGenState.symbols = createMockSymbols({
|
|
322
|
+
knownEnums: new Set(), // No enums
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const mockCtx = { getText: () => "Motor.State.IDLE" };
|
|
326
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("returns null for single identifier that is not in type registry", () => {
|
|
330
|
+
CodeGenState.symbols = createMockSymbols();
|
|
331
|
+
|
|
332
|
+
const mockCtx = { getText: () => "unknownVar" };
|
|
333
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for SizeofResolver - sizeof expression generation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
6
|
+
import SizeofResolver from "../SizeofResolver";
|
|
7
|
+
import CodeGenState from "../../CodeGenState";
|
|
8
|
+
|
|
9
|
+
describe("SizeofResolver", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
CodeGenState.reset();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("sizeofParameter", () => {
|
|
15
|
+
it("throws E0601 for array parameter", () => {
|
|
16
|
+
// Set up parameter info indicating array
|
|
17
|
+
CodeGenState.currentParameters.set("arr", {
|
|
18
|
+
name: "arr",
|
|
19
|
+
baseType: "u32",
|
|
20
|
+
isArray: true,
|
|
21
|
+
isStruct: false,
|
|
22
|
+
isConst: false,
|
|
23
|
+
isCallback: false,
|
|
24
|
+
isString: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const mockCallbacks = {
|
|
28
|
+
generateType: vi.fn(),
|
|
29
|
+
generateExpression: vi.fn(),
|
|
30
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Create a mock context for sizeof(arr)
|
|
34
|
+
const mockTypeCtx = {
|
|
35
|
+
qualifiedType: () => null,
|
|
36
|
+
userType: () => ({ getText: () => "arr" }),
|
|
37
|
+
getText: () => "arr",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const mockCtx = {
|
|
41
|
+
type: () => mockTypeCtx,
|
|
42
|
+
expression: () => null,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
expect(() =>
|
|
46
|
+
SizeofResolver.generate(mockCtx as never, mockCallbacks),
|
|
47
|
+
).toThrow("Error[E0601]");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("generates dereference for pass-by-reference parameter", () => {
|
|
51
|
+
// Non-array, non-callback, non-struct parameter is pass-by-reference
|
|
52
|
+
CodeGenState.currentParameters.set("value", {
|
|
53
|
+
name: "value",
|
|
54
|
+
baseType: "u32",
|
|
55
|
+
isArray: false,
|
|
56
|
+
isStruct: false,
|
|
57
|
+
isConst: false,
|
|
58
|
+
isCallback: false,
|
|
59
|
+
isString: false,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const mockCallbacks = {
|
|
63
|
+
generateType: vi.fn(),
|
|
64
|
+
generateExpression: vi.fn(),
|
|
65
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const mockTypeCtx = {
|
|
69
|
+
qualifiedType: () => null,
|
|
70
|
+
userType: () => ({ getText: () => "value" }),
|
|
71
|
+
getText: () => "value",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const mockCtx = {
|
|
75
|
+
type: () => mockTypeCtx,
|
|
76
|
+
expression: () => null,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
80
|
+
|
|
81
|
+
expect(result).toBe("sizeof(*value)");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("sizeofQualifiedType", () => {
|
|
86
|
+
it("handles struct.member access for local variable", () => {
|
|
87
|
+
CodeGenState.localVariables.add("myStruct");
|
|
88
|
+
|
|
89
|
+
const mockCallbacks = {
|
|
90
|
+
generateType: vi.fn(),
|
|
91
|
+
generateExpression: vi.fn(),
|
|
92
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const mockQualifiedCtx = {
|
|
96
|
+
IDENTIFIER: () => [
|
|
97
|
+
{ getText: () => "myStruct" },
|
|
98
|
+
{ getText: () => "field" },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const mockTypeCtx = {
|
|
103
|
+
qualifiedType: () => mockQualifiedCtx,
|
|
104
|
+
userType: () => null,
|
|
105
|
+
getText: () => "myStruct.field",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const mockCtx = {
|
|
109
|
+
type: () => mockTypeCtx,
|
|
110
|
+
expression: () => null,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
114
|
+
|
|
115
|
+
expect(result).toBe("sizeof(myStruct.field)");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("handles struct parameter with arrow notation", () => {
|
|
119
|
+
CodeGenState.currentParameters.set("param", {
|
|
120
|
+
name: "param",
|
|
121
|
+
baseType: "MyStruct",
|
|
122
|
+
isArray: false,
|
|
123
|
+
isStruct: true,
|
|
124
|
+
isConst: false,
|
|
125
|
+
isCallback: false,
|
|
126
|
+
isString: false,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const mockCallbacks = {
|
|
130
|
+
generateType: vi.fn(),
|
|
131
|
+
generateExpression: vi.fn(),
|
|
132
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const mockQualifiedCtx = {
|
|
136
|
+
IDENTIFIER: () => [
|
|
137
|
+
{ getText: () => "param" },
|
|
138
|
+
{ getText: () => "field" },
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const mockTypeCtx = {
|
|
143
|
+
qualifiedType: () => mockQualifiedCtx,
|
|
144
|
+
userType: () => null,
|
|
145
|
+
getText: () => "param.field",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const mockCtx = {
|
|
149
|
+
type: () => mockTypeCtx,
|
|
150
|
+
expression: () => null,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
154
|
+
|
|
155
|
+
expect(result).toBe("sizeof(param->field)");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("handles non-struct parameter with dot notation", () => {
|
|
159
|
+
CodeGenState.currentParameters.set("param", {
|
|
160
|
+
name: "param",
|
|
161
|
+
baseType: "MyStruct",
|
|
162
|
+
isArray: false,
|
|
163
|
+
isStruct: false,
|
|
164
|
+
isConst: false,
|
|
165
|
+
isCallback: false,
|
|
166
|
+
isString: false,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const mockCallbacks = {
|
|
170
|
+
generateType: vi.fn(),
|
|
171
|
+
generateExpression: vi.fn(),
|
|
172
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const mockQualifiedCtx = {
|
|
176
|
+
IDENTIFIER: () => [
|
|
177
|
+
{ getText: () => "param" },
|
|
178
|
+
{ getText: () => "field" },
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const mockTypeCtx = {
|
|
183
|
+
qualifiedType: () => mockQualifiedCtx,
|
|
184
|
+
userType: () => null,
|
|
185
|
+
getText: () => "param.field",
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const mockCtx = {
|
|
189
|
+
type: () => mockTypeCtx,
|
|
190
|
+
expression: () => null,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
194
|
+
|
|
195
|
+
expect(result).toBe("sizeof(param.field)");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Note: sizeofExpression with side effects is tested via integration tests
|
|
200
|
+
// The mock structure required is too complex for unit testing
|
|
201
|
+
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependencies for TypeResolver - allows TypeResolver to be independent of CodeGenerator
|
|
3
|
-
* Issue #61: Extracted dependencies for better separation of concerns
|
|
4
|
-
*/
|
|
5
|
-
import ICodeGenSymbols from "../../../types/ICodeGenSymbols";
|
|
6
|
-
import SymbolTable from "../../../logic/symbols/SymbolTable";
|
|
7
|
-
import TTypeInfo from "./TTypeInfo";
|
|
8
|
-
|
|
9
|
-
interface ITypeResolverDeps {
|
|
10
|
-
/** Symbol information from C-Next source (ADR-055: ICodeGenSymbols) */
|
|
11
|
-
symbols: ICodeGenSymbols | null;
|
|
12
|
-
|
|
13
|
-
/** Symbol table for C header struct lookups */
|
|
14
|
-
symbolTable: SymbolTable | null;
|
|
15
|
-
|
|
16
|
-
/** Type registry for variable type information */
|
|
17
|
-
typeRegistry: Map<string, TTypeInfo>;
|
|
18
|
-
|
|
19
|
-
/** Callback to resolve identifiers to scoped names */
|
|
20
|
-
resolveIdentifier: (name: string) => string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default ITypeResolverDeps;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependencies for TypeValidator - allows TypeValidator to be independent of CodeGenerator
|
|
3
|
-
* Issue #63: Extracted dependencies for better separation of concerns
|
|
4
|
-
*/
|
|
5
|
-
import ICodeGenSymbols from "../../../types/ICodeGenSymbols";
|
|
6
|
-
import SymbolTable from "../../../logic/symbols/SymbolTable";
|
|
7
|
-
import TTypeInfo from "./TTypeInfo";
|
|
8
|
-
import TParameterInfo from "./TParameterInfo";
|
|
9
|
-
import ICallbackTypeInfo from "./ICallbackTypeInfo";
|
|
10
|
-
import TypeResolver from "../TypeResolver";
|
|
11
|
-
|
|
12
|
-
interface ITypeValidatorDeps {
|
|
13
|
-
/** Symbol information from C-Next source (ADR-055: ICodeGenSymbols) */
|
|
14
|
-
symbols: ICodeGenSymbols | null;
|
|
15
|
-
|
|
16
|
-
/** Symbol table for C header struct lookups */
|
|
17
|
-
symbolTable: SymbolTable | null;
|
|
18
|
-
|
|
19
|
-
/** Type registry for variable type information */
|
|
20
|
-
typeRegistry: Map<string, TTypeInfo>;
|
|
21
|
-
|
|
22
|
-
/** Type resolver for type checking operations (Issue #61) */
|
|
23
|
-
typeResolver: TypeResolver;
|
|
24
|
-
|
|
25
|
-
/** Callback type definitions for signature validation */
|
|
26
|
-
callbackTypes: Map<string, ICallbackTypeInfo>;
|
|
27
|
-
|
|
28
|
-
/** Known function names in the program */
|
|
29
|
-
knownFunctions: Set<string>;
|
|
30
|
-
|
|
31
|
-
/** Known global variable names */
|
|
32
|
-
knownGlobals: Set<string>;
|
|
33
|
-
|
|
34
|
-
/** Callback to get current scope name */
|
|
35
|
-
getCurrentScope: () => string | null;
|
|
36
|
-
|
|
37
|
-
/** Callback to get scope members map */
|
|
38
|
-
getScopeMembers: () => Map<string, Set<string>>;
|
|
39
|
-
|
|
40
|
-
/** Callback to get current function parameters */
|
|
41
|
-
getCurrentParameters: () => Map<string, TParameterInfo>;
|
|
42
|
-
|
|
43
|
-
/** Callback to get local variables in current function */
|
|
44
|
-
getLocalVariables: () => Set<string>;
|
|
45
|
-
|
|
46
|
-
/** Callback to resolve identifiers to scoped names */
|
|
47
|
-
resolveIdentifier: (name: string) => string;
|
|
48
|
-
|
|
49
|
-
/** Callback to get expression type */
|
|
50
|
-
getExpressionType: (ctx: unknown) => string | null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default ITypeValidatorDeps;
|