c-next 0.1.65 → 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.
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Unit tests for ParameterSignatureBuilder
3
+ *
4
+ * Tests the stateless builder that generates C/C++ parameter signatures
5
+ * from normalized IParameterInput.
6
+ */
7
+
8
+ import { describe, it, expect } from "vitest";
9
+ import ParameterSignatureBuilder from "../ParameterSignatureBuilder";
10
+ import IParameterInput from "../../types/IParameterInput";
11
+
12
+ /**
13
+ * Helper to create a minimal IParameterInput with defaults
14
+ */
15
+ function createInput(overrides: Partial<IParameterInput>): IParameterInput {
16
+ return {
17
+ name: "param",
18
+ baseType: "u32",
19
+ mappedType: "uint32_t",
20
+ isConst: false,
21
+ isAutoConst: false,
22
+ isArray: false,
23
+ isCallback: false,
24
+ isString: false,
25
+ isPassByValue: false,
26
+ isPassByReference: true,
27
+ ...overrides,
28
+ };
29
+ }
30
+
31
+ describe("ParameterSignatureBuilder", () => {
32
+ describe("callback parameters", () => {
33
+ it("generates callback typedef name", () => {
34
+ const input = createInput({
35
+ name: "onClick",
36
+ baseType: "handleClick",
37
+ isCallback: true,
38
+ callbackTypedefName: "HandleClickCallback",
39
+ });
40
+
41
+ const result = ParameterSignatureBuilder.build(input, "*");
42
+
43
+ expect(result).toBe("HandleClickCallback onClick");
44
+ });
45
+ });
46
+
47
+ describe("array parameters", () => {
48
+ it("generates single dimension array", () => {
49
+ const input = createInput({
50
+ name: "arr",
51
+ baseType: "u32",
52
+ mappedType: "uint32_t",
53
+ isArray: true,
54
+ arrayDimensions: ["10"],
55
+ isAutoConst: true,
56
+ });
57
+
58
+ const result = ParameterSignatureBuilder.build(input, "*");
59
+
60
+ expect(result).toBe("const uint32_t arr[10]");
61
+ });
62
+
63
+ it("generates multi-dimensional array", () => {
64
+ const input = createInput({
65
+ name: "matrix",
66
+ baseType: "u8",
67
+ mappedType: "uint8_t",
68
+ isArray: true,
69
+ arrayDimensions: ["4", "4"],
70
+ isAutoConst: true,
71
+ });
72
+
73
+ const result = ParameterSignatureBuilder.build(input, "*");
74
+
75
+ expect(result).toBe("const uint8_t matrix[4][4]");
76
+ });
77
+
78
+ it("generates bounded string array with capacity dimension", () => {
79
+ const input = createInput({
80
+ name: "names",
81
+ baseType: "string<32>",
82
+ mappedType: "char",
83
+ isArray: true,
84
+ arrayDimensions: ["5", "33"],
85
+ isString: true,
86
+ isAutoConst: true,
87
+ });
88
+
89
+ const result = ParameterSignatureBuilder.build(input, "*");
90
+
91
+ expect(result).toBe("const char names[5][33]");
92
+ });
93
+
94
+ it("generates unbounded string array with char*", () => {
95
+ const input = createInput({
96
+ name: "strings",
97
+ baseType: "string",
98
+ mappedType: "char",
99
+ isArray: true,
100
+ arrayDimensions: ["5"],
101
+ isString: true,
102
+ isUnboundedString: true,
103
+ isAutoConst: true,
104
+ });
105
+
106
+ const result = ParameterSignatureBuilder.build(input, "*");
107
+
108
+ expect(result).toBe("const char* strings[5]");
109
+ });
110
+
111
+ it("omits auto-const for modified array parameter", () => {
112
+ const input = createInput({
113
+ name: "arr",
114
+ baseType: "u32",
115
+ mappedType: "uint32_t",
116
+ isArray: true,
117
+ arrayDimensions: ["10"],
118
+ isAutoConst: false, // parameter is modified
119
+ });
120
+
121
+ const result = ParameterSignatureBuilder.build(input, "*");
122
+
123
+ expect(result).toBe("uint32_t arr[10]");
124
+ });
125
+ });
126
+
127
+ describe("pass-by-value parameters", () => {
128
+ it("generates ISR parameter", () => {
129
+ const input = createInput({
130
+ name: "handler",
131
+ baseType: "ISR",
132
+ mappedType: "ISR",
133
+ isPassByValue: true,
134
+ });
135
+
136
+ const result = ParameterSignatureBuilder.build(input, "*");
137
+
138
+ expect(result).toBe("ISR handler");
139
+ });
140
+
141
+ it("generates float parameter", () => {
142
+ const input = createInput({
143
+ name: "value",
144
+ baseType: "f32",
145
+ mappedType: "float",
146
+ isPassByValue: true,
147
+ });
148
+
149
+ const result = ParameterSignatureBuilder.build(input, "*");
150
+
151
+ expect(result).toBe("float value");
152
+ });
153
+
154
+ it("generates enum parameter", () => {
155
+ const input = createInput({
156
+ name: "status",
157
+ baseType: "Status",
158
+ mappedType: "Status",
159
+ isPassByValue: true,
160
+ });
161
+
162
+ const result = ParameterSignatureBuilder.build(input, "*");
163
+
164
+ expect(result).toBe("Status status");
165
+ });
166
+
167
+ it("preserves explicit const on pass-by-value", () => {
168
+ const input = createInput({
169
+ name: "value",
170
+ baseType: "f32",
171
+ mappedType: "float",
172
+ isConst: true,
173
+ isPassByValue: true,
174
+ });
175
+
176
+ const result = ParameterSignatureBuilder.build(input, "*");
177
+
178
+ expect(result).toBe("const float value");
179
+ });
180
+ });
181
+
182
+ describe("non-array string parameters", () => {
183
+ it("generates string parameter as char*", () => {
184
+ const input = createInput({
185
+ name: "name",
186
+ baseType: "string<32>",
187
+ mappedType: "char",
188
+ isString: true,
189
+ isAutoConst: true,
190
+ });
191
+
192
+ const result = ParameterSignatureBuilder.build(input, "*");
193
+
194
+ expect(result).toBe("const char* name");
195
+ });
196
+
197
+ it("omits auto-const for modified string parameter", () => {
198
+ const input = createInput({
199
+ name: "buffer",
200
+ baseType: "string<64>",
201
+ mappedType: "char",
202
+ isString: true,
203
+ isAutoConst: false, // parameter is modified
204
+ });
205
+
206
+ const result = ParameterSignatureBuilder.build(input, "*");
207
+
208
+ expect(result).toBe("char* buffer");
209
+ });
210
+ });
211
+
212
+ describe("pass-by-reference parameters", () => {
213
+ it("generates struct parameter with pointer in C mode", () => {
214
+ const input = createInput({
215
+ name: "point",
216
+ baseType: "Point",
217
+ mappedType: "Point",
218
+ isPassByReference: true,
219
+ isAutoConst: true,
220
+ });
221
+
222
+ const result = ParameterSignatureBuilder.build(input, "*");
223
+
224
+ expect(result).toBe("const Point* point");
225
+ });
226
+
227
+ it("generates struct parameter with reference in C++ mode", () => {
228
+ const input = createInput({
229
+ name: "point",
230
+ baseType: "Point",
231
+ mappedType: "Point",
232
+ isPassByReference: true,
233
+ isAutoConst: true,
234
+ });
235
+
236
+ const result = ParameterSignatureBuilder.build(input, "&");
237
+
238
+ expect(result).toBe("const Point& point");
239
+ });
240
+
241
+ it("omits auto-const for modified struct parameter", () => {
242
+ const input = createInput({
243
+ name: "point",
244
+ baseType: "Point",
245
+ mappedType: "Point",
246
+ isPassByReference: true,
247
+ isAutoConst: false, // parameter is modified
248
+ });
249
+
250
+ const result = ParameterSignatureBuilder.build(input, "*");
251
+
252
+ expect(result).toBe("Point* point");
253
+ });
254
+
255
+ it("generates primitive parameter with pointer", () => {
256
+ const input = createInput({
257
+ name: "value",
258
+ baseType: "u32",
259
+ mappedType: "uint32_t",
260
+ isPassByReference: true,
261
+ isAutoConst: true,
262
+ });
263
+
264
+ const result = ParameterSignatureBuilder.build(input, "*");
265
+
266
+ expect(result).toBe("const uint32_t* value");
267
+ });
268
+
269
+ it("preserves explicit const with auto-const", () => {
270
+ const input = createInput({
271
+ name: "point",
272
+ baseType: "Point",
273
+ mappedType: "Point",
274
+ isConst: true,
275
+ isAutoConst: false, // explicit const already set
276
+ isPassByReference: true,
277
+ });
278
+
279
+ const result = ParameterSignatureBuilder.build(input, "*");
280
+
281
+ expect(result).toBe("const Point* point");
282
+ });
283
+ });
284
+
285
+ describe("unknown type parameters", () => {
286
+ it("generates unknown type as pass-by-value", () => {
287
+ const input = createInput({
288
+ name: "data",
289
+ baseType: "UnknownType",
290
+ mappedType: "UnknownType",
291
+ isPassByReference: false,
292
+ isPassByValue: false,
293
+ });
294
+
295
+ const result = ParameterSignatureBuilder.build(input, "*");
296
+
297
+ expect(result).toBe("UnknownType data");
298
+ });
299
+
300
+ it("preserves const on unknown type", () => {
301
+ const input = createInput({
302
+ name: "data",
303
+ baseType: "UnknownType",
304
+ mappedType: "UnknownType",
305
+ isConst: true,
306
+ isPassByReference: false,
307
+ isPassByValue: false,
308
+ });
309
+
310
+ const result = ParameterSignatureBuilder.build(input, "*");
311
+
312
+ expect(result).toBe("const UnknownType data");
313
+ });
314
+ });
315
+ });
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Unit tests for VariableDeclarationFormatter.
3
+ *
4
+ * Tests the stateless variable declaration formatting used by both
5
+ * CodeGenerator and HeaderGeneratorUtils.
6
+ */
7
+
8
+ import { describe, expect, it } from "vitest";
9
+ import VariableDeclarationFormatter from "../VariableDeclarationFormatter";
10
+ import type IVariableFormatInput from "../../types/IVariableFormatInput";
11
+
12
+ describe("VariableDeclarationFormatter", () => {
13
+ describe("format", () => {
14
+ it("formats simple variable declaration", () => {
15
+ const input: IVariableFormatInput = {
16
+ name: "counter",
17
+ cnextType: "u32",
18
+ mappedType: "uint32_t",
19
+ modifiers: {
20
+ isConst: false,
21
+ isAtomic: false,
22
+ isVolatile: false,
23
+ isExtern: false,
24
+ },
25
+ };
26
+
27
+ expect(VariableDeclarationFormatter.format(input)).toBe(
28
+ "uint32_t counter",
29
+ );
30
+ });
31
+
32
+ it("formats const variable", () => {
33
+ const input: IVariableFormatInput = {
34
+ name: "MAX_SIZE",
35
+ cnextType: "u32",
36
+ mappedType: "uint32_t",
37
+ modifiers: {
38
+ isConst: true,
39
+ isAtomic: false,
40
+ isVolatile: false,
41
+ isExtern: false,
42
+ },
43
+ };
44
+
45
+ expect(VariableDeclarationFormatter.format(input)).toBe(
46
+ "const uint32_t MAX_SIZE",
47
+ );
48
+ });
49
+
50
+ it("formats extern variable", () => {
51
+ const input: IVariableFormatInput = {
52
+ name: "globalCounter",
53
+ cnextType: "u32",
54
+ mappedType: "uint32_t",
55
+ modifiers: {
56
+ isConst: false,
57
+ isAtomic: false,
58
+ isVolatile: false,
59
+ isExtern: true,
60
+ },
61
+ };
62
+
63
+ expect(VariableDeclarationFormatter.format(input)).toBe(
64
+ "extern uint32_t globalCounter",
65
+ );
66
+ });
67
+
68
+ it("formats extern const variable with correct ordering", () => {
69
+ const input: IVariableFormatInput = {
70
+ name: "MAX_SIZE",
71
+ cnextType: "u32",
72
+ mappedType: "uint32_t",
73
+ modifiers: {
74
+ isConst: true,
75
+ isAtomic: false,
76
+ isVolatile: false,
77
+ isExtern: true,
78
+ },
79
+ };
80
+
81
+ // Order should be: extern volatile const (volatile before const)
82
+ expect(VariableDeclarationFormatter.format(input)).toBe(
83
+ "extern const uint32_t MAX_SIZE",
84
+ );
85
+ });
86
+
87
+ it("formats atomic variable (maps to volatile)", () => {
88
+ const input: IVariableFormatInput = {
89
+ name: "sharedCounter",
90
+ cnextType: "u32",
91
+ mappedType: "uint32_t",
92
+ modifiers: {
93
+ isConst: false,
94
+ isAtomic: true,
95
+ isVolatile: false,
96
+ isExtern: false,
97
+ },
98
+ };
99
+
100
+ expect(VariableDeclarationFormatter.format(input)).toBe(
101
+ "volatile uint32_t sharedCounter",
102
+ );
103
+ });
104
+
105
+ it("formats volatile variable", () => {
106
+ const input: IVariableFormatInput = {
107
+ name: "hwRegister",
108
+ cnextType: "u32",
109
+ mappedType: "uint32_t",
110
+ modifiers: {
111
+ isConst: false,
112
+ isAtomic: false,
113
+ isVolatile: true,
114
+ isExtern: false,
115
+ },
116
+ };
117
+
118
+ expect(VariableDeclarationFormatter.format(input)).toBe(
119
+ "volatile uint32_t hwRegister",
120
+ );
121
+ });
122
+
123
+ it("formats extern volatile const with all modifiers", () => {
124
+ const input: IVariableFormatInput = {
125
+ name: "ROM_DATA",
126
+ cnextType: "u32",
127
+ mappedType: "uint32_t",
128
+ modifiers: {
129
+ isConst: true,
130
+ isAtomic: false,
131
+ isVolatile: true,
132
+ isExtern: true,
133
+ },
134
+ };
135
+
136
+ // Order: extern volatile const (volatile before const)
137
+ expect(VariableDeclarationFormatter.format(input)).toBe(
138
+ "extern volatile const uint32_t ROM_DATA",
139
+ );
140
+ });
141
+
142
+ it("formats array variable with single dimension", () => {
143
+ const input: IVariableFormatInput = {
144
+ name: "buffer",
145
+ cnextType: "u8",
146
+ mappedType: "uint8_t",
147
+ modifiers: {
148
+ isConst: false,
149
+ isAtomic: false,
150
+ isVolatile: false,
151
+ isExtern: false,
152
+ },
153
+ arrayDimensions: ["256"],
154
+ };
155
+
156
+ expect(VariableDeclarationFormatter.format(input)).toBe(
157
+ "uint8_t buffer[256]",
158
+ );
159
+ });
160
+
161
+ it("formats array variable with multiple dimensions", () => {
162
+ const input: IVariableFormatInput = {
163
+ name: "matrix",
164
+ cnextType: "u8",
165
+ mappedType: "uint8_t",
166
+ modifiers: {
167
+ isConst: false,
168
+ isAtomic: false,
169
+ isVolatile: false,
170
+ isExtern: false,
171
+ },
172
+ arrayDimensions: ["4", "4"],
173
+ };
174
+
175
+ expect(VariableDeclarationFormatter.format(input)).toBe(
176
+ "uint8_t matrix[4][4]",
177
+ );
178
+ });
179
+
180
+ it("formats string<N> variable with embedded dimension", () => {
181
+ const input: IVariableFormatInput = {
182
+ name: "greeting",
183
+ cnextType: "string<32>",
184
+ mappedType: "char[33]", // string<32> maps to char[33]
185
+ modifiers: {
186
+ isConst: false,
187
+ isAtomic: false,
188
+ isVolatile: false,
189
+ isExtern: false,
190
+ },
191
+ };
192
+
193
+ // Embedded dimension should come after variable name
194
+ expect(VariableDeclarationFormatter.format(input)).toBe(
195
+ "char greeting[33]",
196
+ );
197
+ });
198
+
199
+ it("formats string<N> array with additional dimensions first", () => {
200
+ const input: IVariableFormatInput = {
201
+ name: "names",
202
+ cnextType: "string<32>",
203
+ mappedType: "char[33]",
204
+ modifiers: {
205
+ isConst: false,
206
+ isAtomic: false,
207
+ isVolatile: false,
208
+ isExtern: false,
209
+ },
210
+ arrayDimensions: ["5"],
211
+ };
212
+
213
+ // Additional dims first, then embedded dim
214
+ expect(VariableDeclarationFormatter.format(input)).toBe(
215
+ "char names[5][33]",
216
+ );
217
+ });
218
+
219
+ it("formats const string<N> array for header extern", () => {
220
+ const input: IVariableFormatInput = {
221
+ name: "messages",
222
+ cnextType: "string<64>",
223
+ mappedType: "char[65]",
224
+ modifiers: {
225
+ isConst: true,
226
+ isAtomic: false,
227
+ isVolatile: false,
228
+ isExtern: true,
229
+ },
230
+ arrayDimensions: ["10"],
231
+ };
232
+
233
+ expect(VariableDeclarationFormatter.format(input)).toBe(
234
+ "extern const char messages[10][65]",
235
+ );
236
+ });
237
+
238
+ it("formats user-defined type variable", () => {
239
+ const input: IVariableFormatInput = {
240
+ name: "point",
241
+ cnextType: "Point",
242
+ mappedType: "Point",
243
+ modifiers: {
244
+ isConst: false,
245
+ isAtomic: false,
246
+ isVolatile: false,
247
+ isExtern: false,
248
+ },
249
+ };
250
+
251
+ expect(VariableDeclarationFormatter.format(input)).toBe("Point point");
252
+ });
253
+
254
+ it("handles enum dimension identifiers", () => {
255
+ const input: IVariableFormatInput = {
256
+ name: "colors",
257
+ cnextType: "u8",
258
+ mappedType: "uint8_t",
259
+ modifiers: {
260
+ isConst: false,
261
+ isAtomic: false,
262
+ isVolatile: false,
263
+ isExtern: true,
264
+ },
265
+ arrayDimensions: ["EColor_COUNT"],
266
+ };
267
+
268
+ expect(VariableDeclarationFormatter.format(input)).toBe(
269
+ "extern uint8_t colors[EColor_COUNT]",
270
+ );
271
+ });
272
+ });
273
+
274
+ describe("buildModifierPrefix", () => {
275
+ it("returns empty string for no modifiers", () => {
276
+ expect(
277
+ VariableDeclarationFormatter.buildModifierPrefix({
278
+ isConst: false,
279
+ isAtomic: false,
280
+ isVolatile: false,
281
+ isExtern: false,
282
+ }),
283
+ ).toBe("");
284
+ });
285
+
286
+ it("builds single modifier", () => {
287
+ expect(
288
+ VariableDeclarationFormatter.buildModifierPrefix({
289
+ isConst: true,
290
+ isAtomic: false,
291
+ isVolatile: false,
292
+ isExtern: false,
293
+ }),
294
+ ).toBe("const ");
295
+ });
296
+
297
+ it("builds multiple modifiers in correct order", () => {
298
+ // Order: extern volatile const (volatile before const)
299
+ expect(
300
+ VariableDeclarationFormatter.buildModifierPrefix({
301
+ isConst: true,
302
+ isAtomic: true,
303
+ isVolatile: false,
304
+ isExtern: true,
305
+ }),
306
+ ).toBe("extern volatile const ");
307
+ });
308
+ });
309
+
310
+ describe("buildArrayDimensions", () => {
311
+ it("returns empty string for undefined dimensions", () => {
312
+ expect(VariableDeclarationFormatter.buildArrayDimensions(undefined)).toBe(
313
+ "",
314
+ );
315
+ });
316
+
317
+ it("returns empty string for empty array", () => {
318
+ expect(VariableDeclarationFormatter.buildArrayDimensions([])).toBe("");
319
+ });
320
+
321
+ it("formats single dimension", () => {
322
+ expect(VariableDeclarationFormatter.buildArrayDimensions(["10"])).toBe(
323
+ "[10]",
324
+ );
325
+ });
326
+
327
+ it("formats multiple dimensions", () => {
328
+ expect(
329
+ VariableDeclarationFormatter.buildArrayDimensions(["10", "20", "30"]),
330
+ ).toBe("[10][20][30]");
331
+ });
332
+ });
333
+ });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Normalized parameter input for signature generation
3
+ *
4
+ * This interface serves as the contract between ParameterInputAdapter
5
+ * (which normalizes AST or symbol data) and ParameterSignatureBuilder
6
+ * (which generates the final C/C++ parameter string).
7
+ *
8
+ * All decisions are pre-computed before reaching the builder:
9
+ * - Type classification (callback, string, array, etc.)
10
+ * - Const qualifiers (explicit and auto-inferred)
11
+ * - Pass-by-value vs pass-by-reference
12
+ * - Array dimensions
13
+ */
14
+ interface IParameterInput {
15
+ /** Parameter name */
16
+ name: string;
17
+
18
+ /** Original C-Next type: 'u32', 'string<32>', 'Point', etc. */
19
+ baseType: string;
20
+
21
+ /** Mapped C type: 'uint32_t', 'char', 'Point', etc. */
22
+ mappedType: string;
23
+
24
+ /** Explicit const modifier from source code */
25
+ isConst: boolean;
26
+
27
+ /** Inferred const for unmodified parameters (computed from CodeGenState.modifiedParameters) */
28
+ isAutoConst: boolean;
29
+
30
+ /** Whether this is an array type */
31
+ isArray: boolean;
32
+
33
+ /** Array dimensions as strings: ['10', '20'] or ['33'] for string capacity */
34
+ arrayDimensions?: string[];
35
+
36
+ /** Whether this is a callback type (from CodeGenState.callbackTypes) */
37
+ isCallback: boolean;
38
+
39
+ /** The typedef name for callback types (e.g., 'HandleClickCallback') */
40
+ callbackTypedefName?: string;
41
+
42
+ /** Whether this is a string type (bounded or unbounded) */
43
+ isString: boolean;
44
+
45
+ /** Whether this is an unbounded string (no capacity) */
46
+ isUnboundedString?: boolean;
47
+
48
+ /** String capacity for non-array strings (used for tracking, not output) */
49
+ stringCapacity?: number;
50
+
51
+ /** Whether to use pass-by-value semantics (ISR, float, enum, small primitive) */
52
+ isPassByValue: boolean;
53
+
54
+ /** Whether to use pass-by-reference semantics (known struct or known primitive) */
55
+ isPassByReference: boolean;
56
+ }
57
+
58
+ export default IParameterInput;