c-next 0.2.4 → 0.2.5
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/dist/index.js +331 -37
- package/dist/index.js.map +3 -3
- package/package.json +3 -1
- package/src/transpiler/Transpiler.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +112 -1
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +48 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +18 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +38 -10
- package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +63 -10
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
- package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
- package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
- package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
- package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
- package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
- package/src/transpiler/state/CodeGenState.ts +21 -2
- package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
- package/src/{transpiler/output/codegen/utils → utils}/__tests__/ExpressionUnwrapper.test.ts +2 -2
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for TypedefParamParser
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import TypedefParamParser from "../TypedefParamParser";
|
|
7
|
+
|
|
8
|
+
describe("TypedefParamParser", () => {
|
|
9
|
+
describe("parse", () => {
|
|
10
|
+
it("should parse simple callback with pointer params", () => {
|
|
11
|
+
const result = TypedefParamParser.parse(
|
|
12
|
+
"void (*)(widget_t *, const rect_t *, uint8_t *)",
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
expect(result).not.toBeNull();
|
|
16
|
+
expect(result!.returnType).toBe("void");
|
|
17
|
+
expect(result!.params).toHaveLength(3);
|
|
18
|
+
|
|
19
|
+
expect(result!.params[0].isPointer).toBe(true);
|
|
20
|
+
expect(result!.params[0].isConst).toBe(false);
|
|
21
|
+
expect(result!.params[0].baseType).toBe("widget_t");
|
|
22
|
+
|
|
23
|
+
expect(result!.params[1].isPointer).toBe(true);
|
|
24
|
+
expect(result!.params[1].isConst).toBe(true);
|
|
25
|
+
expect(result!.params[1].baseType).toBe("rect_t");
|
|
26
|
+
|
|
27
|
+
expect(result!.params[2].isPointer).toBe(true);
|
|
28
|
+
expect(result!.params[2].isConst).toBe(false);
|
|
29
|
+
expect(result!.params[2].baseType).toBe("uint8_t");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should parse callback without spaces (C grammar format)", () => {
|
|
33
|
+
// This is the actual format from CResolver - getText() may strip spaces
|
|
34
|
+
const result = TypedefParamParser.parse(
|
|
35
|
+
"void (*)(widget_t*,const rect_t*,uint8_t*)",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(result).not.toBeNull();
|
|
39
|
+
expect(result!.returnType).toBe("void");
|
|
40
|
+
expect(result!.params).toHaveLength(3);
|
|
41
|
+
|
|
42
|
+
expect(result!.params[0].isPointer).toBe(true);
|
|
43
|
+
expect(result!.params[0].isConst).toBe(false);
|
|
44
|
+
expect(result!.params[0].baseType).toBe("widget_t");
|
|
45
|
+
|
|
46
|
+
expect(result!.params[1].isPointer).toBe(true);
|
|
47
|
+
expect(result!.params[1].isConst).toBe(true);
|
|
48
|
+
expect(result!.params[1].baseType).toBe("rect_t");
|
|
49
|
+
|
|
50
|
+
expect(result!.params[2].isPointer).toBe(true);
|
|
51
|
+
expect(result!.params[2].isConst).toBe(false);
|
|
52
|
+
expect(result!.params[2].baseType).toBe("uint8_t");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should parse callback with value params", () => {
|
|
56
|
+
const result = TypedefParamParser.parse("void (*)(Point p)");
|
|
57
|
+
|
|
58
|
+
expect(result).not.toBeNull();
|
|
59
|
+
expect(result!.returnType).toBe("void");
|
|
60
|
+
expect(result!.params).toHaveLength(1);
|
|
61
|
+
|
|
62
|
+
expect(result!.params[0].isPointer).toBe(false);
|
|
63
|
+
expect(result!.params[0].isConst).toBe(false);
|
|
64
|
+
expect(result!.params[0].baseType).toBe("Point");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should parse callback with no params", () => {
|
|
68
|
+
const result = TypedefParamParser.parse("void (*)(void)");
|
|
69
|
+
|
|
70
|
+
expect(result).not.toBeNull();
|
|
71
|
+
expect(result!.returnType).toBe("void");
|
|
72
|
+
expect(result!.params).toHaveLength(0);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should parse callback with primitive return type", () => {
|
|
76
|
+
const result = TypedefParamParser.parse("int (*)(int x, int y)");
|
|
77
|
+
|
|
78
|
+
expect(result).not.toBeNull();
|
|
79
|
+
expect(result!.returnType).toBe("int");
|
|
80
|
+
expect(result!.params).toHaveLength(2);
|
|
81
|
+
|
|
82
|
+
expect(result!.params[0].isPointer).toBe(false);
|
|
83
|
+
expect(result!.params[0].baseType).toBe("int");
|
|
84
|
+
|
|
85
|
+
expect(result!.params[1].isPointer).toBe(false);
|
|
86
|
+
expect(result!.params[1].baseType).toBe("int");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should return null for invalid typedef", () => {
|
|
90
|
+
expect(TypedefParamParser.parse("not a typedef")).toBeNull();
|
|
91
|
+
expect(TypedefParamParser.parse("void foo()")).toBeNull();
|
|
92
|
+
expect(TypedefParamParser.parse("")).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should handle mixed pointer and value params", () => {
|
|
96
|
+
const result = TypedefParamParser.parse(
|
|
97
|
+
"void (*)(Point* p, int count, Rect r)",
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(result).not.toBeNull();
|
|
101
|
+
expect(result!.params).toHaveLength(3);
|
|
102
|
+
|
|
103
|
+
expect(result!.params[0].isPointer).toBe(true);
|
|
104
|
+
expect(result!.params[0].baseType).toBe("Point");
|
|
105
|
+
|
|
106
|
+
expect(result!.params[1].isPointer).toBe(false);
|
|
107
|
+
expect(result!.params[1].baseType).toBe("int");
|
|
108
|
+
|
|
109
|
+
expect(result!.params[2].isPointer).toBe(false);
|
|
110
|
+
expect(result!.params[2].baseType).toBe("Rect");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should parse callback with nested function pointer param", () => {
|
|
114
|
+
// Nested function pointer: void (*)(void (*)(int))
|
|
115
|
+
const result = TypedefParamParser.parse("void (*)(void (*)(int))");
|
|
116
|
+
|
|
117
|
+
expect(result).not.toBeNull();
|
|
118
|
+
expect(result!.returnType).toBe("void");
|
|
119
|
+
expect(result!.params).toHaveLength(1);
|
|
120
|
+
|
|
121
|
+
// The param is itself a function pointer
|
|
122
|
+
expect(result!.params[0].type).toBe("void (*)(int)");
|
|
123
|
+
expect(result!.params[0].isPointer).toBe(true); // Function pointers are pointers
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should parse callback with deeply nested function pointer", () => {
|
|
127
|
+
// Two levels of nesting: void (*)(void (*)(void (*)(int)))
|
|
128
|
+
const result = TypedefParamParser.parse(
|
|
129
|
+
"void (*)(void (*)(void (*)(int)))",
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(result).not.toBeNull();
|
|
133
|
+
expect(result!.returnType).toBe("void");
|
|
134
|
+
expect(result!.params).toHaveLength(1);
|
|
135
|
+
expect(result!.params[0].type).toBe("void (*)(void (*)(int))");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should parse callback with mixed nested and regular params", () => {
|
|
139
|
+
// Mix of nested function pointer and regular params
|
|
140
|
+
const result = TypedefParamParser.parse(
|
|
141
|
+
"void (*)(int x, void (*)(int), char* str)",
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(result).not.toBeNull();
|
|
145
|
+
expect(result!.returnType).toBe("void");
|
|
146
|
+
expect(result!.params).toHaveLength(3);
|
|
147
|
+
|
|
148
|
+
expect(result!.params[0].baseType).toBe("int");
|
|
149
|
+
expect(result!.params[0].isPointer).toBe(false);
|
|
150
|
+
|
|
151
|
+
expect(result!.params[1].type).toBe("void (*)(int)");
|
|
152
|
+
expect(result!.params[1].isPointer).toBe(true);
|
|
153
|
+
|
|
154
|
+
expect(result!.params[2].baseType).toBe("char");
|
|
155
|
+
expect(result!.params[2].isPointer).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("shouldBePointer", () => {
|
|
160
|
+
it("should return true for pointer params", () => {
|
|
161
|
+
const typedef = "void (*)(widget_t *, uint8_t *)";
|
|
162
|
+
|
|
163
|
+
expect(TypedefParamParser.shouldBePointer(typedef, 0)).toBe(true);
|
|
164
|
+
expect(TypedefParamParser.shouldBePointer(typedef, 1)).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("should return false for value params", () => {
|
|
168
|
+
const typedef = "void (*)(Point p, int count)";
|
|
169
|
+
|
|
170
|
+
expect(TypedefParamParser.shouldBePointer(typedef, 0)).toBe(false);
|
|
171
|
+
expect(TypedefParamParser.shouldBePointer(typedef, 1)).toBe(false);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("should return null for out of bounds index", () => {
|
|
175
|
+
const typedef = "void (*)(Point p)";
|
|
176
|
+
|
|
177
|
+
expect(TypedefParamParser.shouldBePointer(typedef, 1)).toBeNull();
|
|
178
|
+
expect(TypedefParamParser.shouldBePointer(typedef, 99)).toBeNull();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should return null for invalid typedef", () => {
|
|
182
|
+
expect(TypedefParamParser.shouldBePointer("invalid", 0)).toBeNull();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("shouldBeConst", () => {
|
|
187
|
+
it("should return true for const params", () => {
|
|
188
|
+
const typedef = "void (*)(const Point* p, const char* s)";
|
|
189
|
+
|
|
190
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 0)).toBe(true);
|
|
191
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 1)).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should return false for non-const params", () => {
|
|
195
|
+
const typedef = "void (*)(Point* p, uint8_t* buf)";
|
|
196
|
+
|
|
197
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 0)).toBe(false);
|
|
198
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 1)).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should handle mixed const params", () => {
|
|
202
|
+
const typedef = "void (*)(widget_t* w, const rect_t* area, uint8_t* buf)";
|
|
203
|
+
|
|
204
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 0)).toBe(false);
|
|
205
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 1)).toBe(true);
|
|
206
|
+
expect(TypedefParamParser.shouldBeConst(typedef, 2)).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
18
18
|
import CodeGenState from "../../../state/CodeGenState";
|
|
19
19
|
import TypeResolver from "../TypeResolver";
|
|
20
|
-
import ExpressionUnwrapper from "
|
|
20
|
+
import ExpressionUnwrapper from "../../../../utils/ExpressionUnwrapper";
|
|
21
21
|
import QualifiedNameGenerator from "../utils/QualifiedNameGenerator";
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
13
13
|
import CodeGenState from "../../../state/CodeGenState";
|
|
14
|
-
import ExpressionUnwrapper from "
|
|
14
|
+
import ExpressionUnwrapper from "../../../../utils/ExpressionUnwrapper";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Callbacks for operations that require CodeGenerator context.
|
|
@@ -53,6 +53,19 @@ interface IParameterInput {
|
|
|
53
53
|
|
|
54
54
|
/** Whether to use pass-by-reference semantics (known struct or known primitive) */
|
|
55
55
|
isPassByReference: boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Issue #895: Force pointer syntax even in C++ mode.
|
|
59
|
+
* Required for callback-compatible functions because C callback typedefs
|
|
60
|
+
* expect pointers, not C++ references.
|
|
61
|
+
*/
|
|
62
|
+
forcePointerSyntax?: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Issue #895: Force const qualifier from callback typedef signature.
|
|
66
|
+
* When the C typedef has `const T*`, this preserves const on the generated param.
|
|
67
|
+
*/
|
|
68
|
+
forceConst?: boolean;
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
export default IParameterInput;
|
|
@@ -23,6 +23,13 @@ interface ISeparatorContext {
|
|
|
23
23
|
|
|
24
24
|
/** Whether scopedRegName refers to a known register */
|
|
25
25
|
readonly isScopedRegister: boolean;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Issue #895: Force pointer semantics even in C++ mode.
|
|
29
|
+
* When true, struct params use -> instead of . because they're part of
|
|
30
|
+
* a callback-compatible function that must match C typedef signatures.
|
|
31
|
+
*/
|
|
32
|
+
readonly forcePointerSemantics?: boolean;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
export default ISeparatorContext;
|
|
@@ -14,6 +14,18 @@ type TParameterInfo = {
|
|
|
14
14
|
isConst: boolean; // ADR-013
|
|
15
15
|
isCallback: boolean; // ADR-029
|
|
16
16
|
isString: boolean; // ADR-045
|
|
17
|
+
/**
|
|
18
|
+
* Issue #895: True when a primitive param becomes a pointer due to callback typedef.
|
|
19
|
+
* When used as a value in expressions, these params need dereferencing (*param).
|
|
20
|
+
*/
|
|
21
|
+
isCallbackPointerPrimitive?: boolean;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Issue #895: True when a param needs pointer semantics due to callback typedef.
|
|
25
|
+
* In C++ mode, this forces -> member access instead of . (reference access).
|
|
26
|
+
* Applies to both struct and primitive callback-compatible params.
|
|
27
|
+
*/
|
|
28
|
+
forcePointerSemantics?: boolean;
|
|
17
29
|
};
|
|
18
30
|
|
|
19
31
|
export default TParameterInfo;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { ParserRuleContext, TerminalNode } from "antlr4ng";
|
|
9
9
|
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
10
|
-
import ExpressionUnwrapper from "
|
|
10
|
+
import ExpressionUnwrapper from "../../../../utils/ExpressionUnwrapper";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Static utility methods for parser context operations in code generation.
|
|
@@ -128,8 +128,12 @@ export default class CodeGenState {
|
|
|
128
128
|
/** Callback field types: "Struct.field" -> callbackTypeName */
|
|
129
129
|
static callbackFieldTypes: Map<string, string> = new Map();
|
|
130
130
|
|
|
131
|
-
/**
|
|
132
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Functions that are assigned to C callback typedefs.
|
|
133
|
+
* Maps function name -> typedef name (e.g., "my_flush" -> "flush_cb_t")
|
|
134
|
+
* Issue #895: We need the typedef name to look up parameter types.
|
|
135
|
+
*/
|
|
136
|
+
static callbackCompatibleFunctions: Map<string, string> = new Map();
|
|
133
137
|
|
|
134
138
|
// ===========================================================================
|
|
135
139
|
// PASS-BY-VALUE ANALYSIS (Issue #269)
|
|
@@ -624,6 +628,21 @@ export default class CodeGenState {
|
|
|
624
628
|
return this.callbackTypes.get(name);
|
|
625
629
|
}
|
|
626
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Issue #895: Get the typedef type string for a C typedef by name.
|
|
633
|
+
* Used to look up function pointer typedef signatures for callback-compatible functions.
|
|
634
|
+
*
|
|
635
|
+
* @param typedefName - Name of the typedef (e.g., "flush_cb_t")
|
|
636
|
+
* @returns The type string (e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)") or undefined
|
|
637
|
+
*/
|
|
638
|
+
static getTypedefType(typedefName: string): string | undefined {
|
|
639
|
+
const symbol = this.symbolTable.getCSymbol(typedefName);
|
|
640
|
+
if (symbol?.kind === "type") {
|
|
641
|
+
return symbol.type;
|
|
642
|
+
}
|
|
643
|
+
return undefined;
|
|
644
|
+
}
|
|
645
|
+
|
|
627
646
|
/**
|
|
628
647
|
* Check if a type name is a known C-Next function.
|
|
629
648
|
*/
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* eliminate code duplication.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import * as Parser from "
|
|
19
|
+
import * as Parser from "../transpiler/logic/parser/grammar/CNextParser";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Utility class for navigating expression tree hierarchy
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, it, expect } from "vitest";
|
|
6
6
|
import ExpressionUnwrapper from "../ExpressionUnwrapper";
|
|
7
|
-
import CNextSourceParser from "
|
|
8
|
-
import * as Parser from "
|
|
7
|
+
import CNextSourceParser from "../../transpiler/logic/parser/CNextSourceParser";
|
|
8
|
+
import * as Parser from "../../transpiler/logic/parser/grammar/CNextParser";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Helper to parse a simple expression and get its ExpressionContext
|