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.
Files changed (31) hide show
  1. package/dist/index.js +331 -37
  2. package/dist/index.js.map +3 -3
  3. package/package.json +3 -1
  4. package/src/transpiler/Transpiler.ts +1 -1
  5. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +112 -1
  6. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +48 -0
  7. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +18 -0
  8. package/src/transpiler/output/codegen/CodeGenerator.ts +38 -10
  9. package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
  10. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
  11. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +63 -10
  12. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
  13. package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
  14. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
  15. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
  16. package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
  17. package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
  18. package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
  19. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
  20. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
  21. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
  22. package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
  23. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
  24. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
  25. package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
  26. package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
  27. package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
  28. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
  29. package/src/transpiler/state/CodeGenState.ts +21 -2
  30. package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
  31. 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 "../utils/ExpressionUnwrapper";
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 "../utils/ExpressionUnwrapper";
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 "./ExpressionUnwrapper";
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
- /** Functions that need C-callback-compatible (by-value) struct parameters */
132
- static callbackCompatibleFunctions: Set<string> = new Set();
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 "../../../logic/parser/grammar/CNextParser";
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 "../../../../logic/parser/CNextSourceParser";
8
- import * as Parser from "../../../../logic/parser/grammar/CNextParser";
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