c-next 0.1.64 → 0.1.66

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.
Files changed (33) hide show
  1. package/grammar/CNext.g4 +9 -2
  2. package/package.json +5 -1
  3. package/src/transpiler/logic/parser/grammar/CNext.interp +2 -1
  4. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  5. package/src/transpiler/logic/parser/grammar/CNextParser.ts +992 -870
  6. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  7. package/src/transpiler/logic/symbols/cnext/__tests__/FunctionCollector.test.ts +6 -6
  8. package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolAdapter.test.ts +179 -0
  9. package/src/transpiler/logic/symbols/cnext/__tests__/VariableCollector.test.ts +55 -0
  10. package/src/transpiler/logic/symbols/cnext/adapters/TSymbolAdapter.ts +76 -8
  11. package/src/transpiler/logic/symbols/cnext/collectors/FunctionCollector.ts +9 -10
  12. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +7 -1
  13. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +33 -14
  14. package/src/transpiler/output/codegen/CodeGenerator.ts +243 -166
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +1086 -0
  16. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +254 -22
  17. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +17 -9
  18. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +5 -3
  19. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +12 -7
  20. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +624 -12
  21. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +819 -0
  22. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +337 -0
  23. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +135 -0
  24. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +4 -0
  25. package/src/transpiler/output/codegen/helpers/VariableDeclarationFormatter.ts +118 -0
  26. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +426 -0
  27. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +315 -0
  28. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclarationFormatter.test.ts +333 -0
  29. package/src/transpiler/output/codegen/types/IParameterInput.ts +58 -0
  30. package/src/transpiler/output/codegen/types/IVariableFormatInput.ts +51 -0
  31. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +20 -35
  32. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +21 -48
  33. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +0 -64
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Unit tests for ParameterInputAdapter
3
+ *
4
+ * Tests the adapter that normalizes AST and symbol data into IParameterInput.
5
+ */
6
+
7
+ import { describe, it, expect } from "vitest";
8
+ import ParameterInputAdapter from "../ParameterInputAdapter";
9
+ import IParameterSymbol from "../../../../../utils/types/IParameterSymbol";
10
+ import CNextSourceParser from "../../../../logic/parser/CNextSourceParser.js";
11
+ import * as Parser from "../../../../logic/parser/grammar/CNextParser.js";
12
+ import ICallbackTypeInfo from "../../types/ICallbackTypeInfo";
13
+
14
+ /**
15
+ * Extract the first parameter context from a function declaration.
16
+ */
17
+ function getParameterContext(source: string): Parser.ParameterContext {
18
+ const result = CNextSourceParser.parse(source);
19
+ const decl = result.tree.declaration(0);
20
+ const funcDecl = decl?.functionDeclaration();
21
+ const paramList = funcDecl?.parameterList();
22
+ const params = paramList?.parameter();
23
+ if (!params || params.length === 0) {
24
+ throw new Error("No parameters found in parsed source");
25
+ }
26
+ return params[0];
27
+ }
28
+
29
+ /**
30
+ * Build default IFromASTDeps for testing.
31
+ */
32
+ function createDefaultASTDeps(overrides?: {
33
+ isModified?: boolean;
34
+ isPassByValue?: boolean;
35
+ isKnownStruct?: boolean;
36
+ callbackTypes?: ReadonlyMap<string, ICallbackTypeInfo>;
37
+ }) {
38
+ const typeMap: Record<string, string> = {
39
+ u8: "uint8_t",
40
+ u16: "uint16_t",
41
+ u32: "uint32_t",
42
+ u64: "uint64_t",
43
+ i32: "int32_t",
44
+ f32: "float",
45
+ f64: "double",
46
+ bool: "bool",
47
+ };
48
+
49
+ return {
50
+ getTypeName: (type: Parser.TypeContext) => type.getText(),
51
+ generateType: (type: Parser.TypeContext) => {
52
+ const text = type.getText();
53
+ // Strip array dimensions for mapped type
54
+ const baseName = text.replace(/\[.*$/, "");
55
+ return typeMap[baseName] ?? baseName;
56
+ },
57
+ generateExpression: (expr: Parser.ExpressionContext) => expr.getText(),
58
+ callbackTypes:
59
+ overrides?.callbackTypes ?? new Map<string, ICallbackTypeInfo>(),
60
+ isKnownStruct: () => overrides?.isKnownStruct ?? false,
61
+ typeMap,
62
+ isModified: overrides?.isModified ?? false,
63
+ isPassByValue: overrides?.isPassByValue ?? false,
64
+ };
65
+ }
66
+
67
+ describe("ParameterInputAdapter", () => {
68
+ describe("fromSymbol", () => {
69
+ const defaultDeps = {
70
+ mapType: (t: string) => {
71
+ const map: Record<string, string> = {
72
+ u8: "uint8_t",
73
+ u16: "uint16_t",
74
+ u32: "uint32_t",
75
+ u64: "uint64_t",
76
+ i32: "int32_t",
77
+ f32: "float",
78
+ f64: "double",
79
+ bool: "bool",
80
+ ISR: "ISR",
81
+ };
82
+ return map[t] ?? t;
83
+ },
84
+ isPassByValue: false,
85
+ };
86
+
87
+ it("converts basic primitive parameter", () => {
88
+ const param: IParameterSymbol = {
89
+ name: "value",
90
+ type: "u32",
91
+ isConst: false,
92
+ isArray: false,
93
+ };
94
+
95
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
96
+
97
+ expect(result.name).toBe("value");
98
+ expect(result.baseType).toBe("u32");
99
+ expect(result.mappedType).toBe("uint32_t");
100
+ expect(result.isConst).toBe(false);
101
+ expect(result.isArray).toBe(false);
102
+ expect(result.isString).toBe(false);
103
+ expect(result.isPassByValue).toBe(false);
104
+ });
105
+
106
+ it("converts array parameter", () => {
107
+ const param: IParameterSymbol = {
108
+ name: "arr",
109
+ type: "u32",
110
+ isConst: false,
111
+ isArray: true,
112
+ arrayDimensions: ["10"],
113
+ };
114
+
115
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
116
+
117
+ expect(result.isArray).toBe(true);
118
+ expect(result.arrayDimensions).toEqual(["10"]);
119
+ expect(result.isString).toBe(false);
120
+ });
121
+
122
+ it("converts multi-dimensional array parameter", () => {
123
+ const param: IParameterSymbol = {
124
+ name: "matrix",
125
+ type: "u8",
126
+ isConst: false,
127
+ isArray: true,
128
+ arrayDimensions: ["4", "4"],
129
+ };
130
+
131
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
132
+
133
+ expect(result.isArray).toBe(true);
134
+ expect(result.arrayDimensions).toEqual(["4", "4"]);
135
+ });
136
+
137
+ it("converts unbounded string array with isUnboundedString flag", () => {
138
+ const param: IParameterSymbol = {
139
+ name: "strings",
140
+ type: "string",
141
+ isConst: false,
142
+ isArray: true,
143
+ arrayDimensions: ["5"],
144
+ };
145
+
146
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
147
+
148
+ expect(result.isArray).toBe(true);
149
+ expect(result.isString).toBe(true);
150
+ expect(result.isUnboundedString).toBe(true);
151
+ expect(result.arrayDimensions).toEqual(["5"]);
152
+ expect(result.mappedType).toBe("char");
153
+ });
154
+
155
+ it("converts bounded string array without isUnboundedString flag", () => {
156
+ const param: IParameterSymbol = {
157
+ name: "names",
158
+ type: "string<32>",
159
+ isConst: false,
160
+ isArray: true,
161
+ arrayDimensions: ["5", "33"],
162
+ };
163
+
164
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
165
+
166
+ expect(result.isArray).toBe(true);
167
+ expect(result.isString).toBe(true);
168
+ expect(result.isUnboundedString).toBe(false);
169
+ expect(result.arrayDimensions).toEqual(["5", "33"]);
170
+ expect(result.mappedType).toBe("char");
171
+ });
172
+
173
+ it("converts non-array string parameter", () => {
174
+ const param: IParameterSymbol = {
175
+ name: "name",
176
+ type: "string<32>",
177
+ isConst: false,
178
+ isArray: false,
179
+ };
180
+
181
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
182
+
183
+ expect(result.isArray).toBe(false);
184
+ expect(result.isString).toBe(true);
185
+ expect(result.mappedType).toBe("char");
186
+ });
187
+
188
+ it("uses pass-by-value from deps for ISR", () => {
189
+ const param: IParameterSymbol = {
190
+ name: "handler",
191
+ type: "ISR",
192
+ isConst: false,
193
+ isArray: false,
194
+ };
195
+
196
+ const deps = { ...defaultDeps, isPassByValue: true };
197
+ const result = ParameterInputAdapter.fromSymbol(param, deps);
198
+
199
+ expect(result.isPassByValue).toBe(true);
200
+ expect(result.isPassByReference).toBe(false);
201
+ });
202
+
203
+ it("uses pass-by-value from deps for float types", () => {
204
+ const param: IParameterSymbol = {
205
+ name: "value",
206
+ type: "f32",
207
+ isConst: false,
208
+ isArray: false,
209
+ };
210
+
211
+ const deps = { ...defaultDeps, isPassByValue: true };
212
+ const result = ParameterInputAdapter.fromSymbol(param, deps);
213
+
214
+ expect(result.isPassByValue).toBe(true);
215
+ expect(result.isPassByReference).toBe(false);
216
+ });
217
+
218
+ it("uses pass-by-value from deps for enums", () => {
219
+ const param: IParameterSymbol = {
220
+ name: "status",
221
+ type: "Status",
222
+ isConst: false,
223
+ isArray: false,
224
+ };
225
+
226
+ const deps = { ...defaultDeps, isPassByValue: true };
227
+ const result = ParameterInputAdapter.fromSymbol(param, deps);
228
+
229
+ expect(result.isPassByValue).toBe(true);
230
+ expect(result.isPassByReference).toBe(false);
231
+ });
232
+
233
+ it("sets isPassByReference when not pass-by-value", () => {
234
+ const param: IParameterSymbol = {
235
+ name: "count",
236
+ type: "u32",
237
+ isConst: false,
238
+ isArray: false,
239
+ };
240
+
241
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
242
+
243
+ expect(result.isPassByValue).toBe(false);
244
+ expect(result.isPassByReference).toBe(true);
245
+ });
246
+
247
+ it("preserves const modifier", () => {
248
+ const param: IParameterSymbol = {
249
+ name: "value",
250
+ type: "u32",
251
+ isConst: true,
252
+ isArray: false,
253
+ };
254
+
255
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
256
+
257
+ expect(result.isConst).toBe(true);
258
+ });
259
+
260
+ it("preserves auto-const", () => {
261
+ const param: IParameterSymbol = {
262
+ name: "point",
263
+ type: "Point",
264
+ isConst: false,
265
+ isArray: false,
266
+ isAutoConst: true,
267
+ };
268
+
269
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
270
+
271
+ expect(result.isAutoConst).toBe(true);
272
+ });
273
+
274
+ it("defaults auto-const to false when not specified", () => {
275
+ const param: IParameterSymbol = {
276
+ name: "point",
277
+ type: "Point",
278
+ isConst: false,
279
+ isArray: false,
280
+ // isAutoConst not specified
281
+ };
282
+
283
+ const result = ParameterInputAdapter.fromSymbol(param, defaultDeps);
284
+
285
+ expect(result.isAutoConst).toBe(false);
286
+ });
287
+ });
288
+
289
+ describe("fromAST", () => {
290
+ it("converts basic primitive parameter", () => {
291
+ const ctx = getParameterContext("void foo(u32 value) {}");
292
+ const deps = createDefaultASTDeps();
293
+
294
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
295
+
296
+ expect(result.name).toBe("value");
297
+ expect(result.baseType).toBe("u32");
298
+ expect(result.mappedType).toBe("uint32_t");
299
+ expect(result.isConst).toBe(false);
300
+ expect(result.isArray).toBe(false);
301
+ expect(result.isString).toBe(false);
302
+ expect(result.isCallback).toBe(false);
303
+ expect(result.isPassByValue).toBe(false);
304
+ expect(result.isPassByReference).toBe(true);
305
+ });
306
+
307
+ it("converts const parameter", () => {
308
+ const ctx = getParameterContext("void foo(const u32 value) {}");
309
+ const deps = createDefaultASTDeps();
310
+
311
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
312
+
313
+ expect(result.isConst).toBe(true);
314
+ expect(result.isAutoConst).toBe(false);
315
+ });
316
+
317
+ it("sets auto-const for unmodified non-const parameter", () => {
318
+ const ctx = getParameterContext("void foo(u32 value) {}");
319
+ const deps = createDefaultASTDeps({ isModified: false });
320
+
321
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
322
+
323
+ expect(result.isAutoConst).toBe(true);
324
+ });
325
+
326
+ it("does not set auto-const for modified parameter", () => {
327
+ const ctx = getParameterContext("void foo(u32 value) {}");
328
+ const deps = createDefaultASTDeps({ isModified: true });
329
+
330
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
331
+
332
+ expect(result.isAutoConst).toBe(false);
333
+ });
334
+
335
+ it("converts array parameter with dimension", () => {
336
+ const ctx = getParameterContext("void foo(u32[10] arr) {}");
337
+ const deps = createDefaultASTDeps();
338
+
339
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
340
+
341
+ expect(result.isArray).toBe(true);
342
+ expect(result.arrayDimensions).toEqual(["10"]);
343
+ expect(result.isPassByValue).toBe(false);
344
+ expect(result.isPassByReference).toBe(false);
345
+ });
346
+
347
+ it("converts multi-dimensional array parameter", () => {
348
+ const ctx = getParameterContext("void foo(u8[4][4] matrix) {}");
349
+ const deps = createDefaultASTDeps();
350
+
351
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
352
+
353
+ expect(result.isArray).toBe(true);
354
+ expect(result.arrayDimensions).toEqual(["4", "4"]);
355
+ });
356
+
357
+ it("converts non-array string parameter", () => {
358
+ const ctx = getParameterContext("void foo(string<32> name) {}");
359
+ const deps = createDefaultASTDeps();
360
+
361
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
362
+
363
+ expect(result.isString).toBe(true);
364
+ expect(result.isArray).toBe(false);
365
+ expect(result.mappedType).toBe("char");
366
+ expect(result.stringCapacity).toBe(32);
367
+ expect(result.isPassByValue).toBe(false);
368
+ expect(result.isPassByReference).toBe(false);
369
+ });
370
+
371
+ it("converts callback parameter", () => {
372
+ const callbackTypes = new Map<string, ICallbackTypeInfo>([
373
+ [
374
+ "handleClick",
375
+ {
376
+ functionName: "handleClick",
377
+ returnType: "void",
378
+ parameters: [],
379
+ typedefName: "handleClick_fp",
380
+ },
381
+ ],
382
+ ]);
383
+ const ctx = getParameterContext("void foo(handleClick onClick) {}");
384
+ const deps = createDefaultASTDeps({ callbackTypes });
385
+
386
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
387
+
388
+ expect(result.isCallback).toBe(true);
389
+ expect(result.callbackTypedefName).toBe("handleClick_fp");
390
+ expect(result.isPassByValue).toBe(true);
391
+ expect(result.isPassByReference).toBe(false);
392
+ });
393
+
394
+ it("sets isPassByReference for known struct", () => {
395
+ const ctx = getParameterContext("void foo(Point p) {}");
396
+ const deps = createDefaultASTDeps({ isKnownStruct: true });
397
+
398
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
399
+
400
+ expect(result.isPassByReference).toBe(true);
401
+ expect(result.isPassByValue).toBe(false);
402
+ });
403
+
404
+ it("sets isPassByValue when pre-computed", () => {
405
+ const ctx = getParameterContext("void foo(f32 value) {}");
406
+ const deps = createDefaultASTDeps({ isPassByValue: true });
407
+
408
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
409
+
410
+ expect(result.isPassByValue).toBe(true);
411
+ });
412
+
413
+ it("converts string array with capacity (string<N>[M])", () => {
414
+ const ctx = getParameterContext("void foo(string<32>[5] names) {}");
415
+ const deps = createDefaultASTDeps();
416
+
417
+ const result = ParameterInputAdapter.fromAST(ctx, deps);
418
+
419
+ expect(result.isArray).toBe(true);
420
+ expect(result.isString).toBe(true);
421
+ // Capacity + 1 for null terminator appended as extra dimension
422
+ expect(result.arrayDimensions).toEqual(["5", "33"]);
423
+ expect(result.isPassByReference).toBe(false);
424
+ });
425
+ });
426
+ });