c-next 0.1.69 → 0.1.70
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/package.json +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +240 -204
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +693 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +86 -5
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/AssignmentTargetExtractor.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/ChildStatementCollector.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/StatementExpressionCollector.ts +1 -1
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/AssignmentTargetExtractor.test.ts +2 -2
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/ChildStatementCollector.test.ts +2 -2
- package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/StatementExpressionCollector.test.ts +2 -2
- package/src/transpiler/output/codegen/CodeGenerator.ts +35 -607
- package/src/transpiler/output/codegen/TypeRegistrationUtils.ts +4 -6
- package/src/transpiler/output/codegen/TypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/TypeValidator.ts +5 -5
- package/src/transpiler/output/codegen/__tests__/TypeRegistrationUtils.test.ts +36 -51
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +20 -17
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +1 -1
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +1 -1
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +1 -1
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +9 -9
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +12 -12
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +11 -11
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +23 -17
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +2 -2
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +3 -3
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +3 -3
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +4 -4
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +5 -5
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +23 -25
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +20 -36
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +18 -18
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +42 -32
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +5 -4
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +14 -6
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +19 -16
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +21 -4
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +15 -2
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +2 -1
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +3 -3
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +1 -1
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +2 -2
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +1 -1
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +7 -7
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +7 -7
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +2 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +4 -4
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +2 -2
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +5 -5
- package/src/transpiler/state/CodeGenState.ts +122 -4
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +269 -1
- /package/src/transpiler/{output/codegen → logic/analysis}/helpers/TransitiveModificationPropagator.ts +0 -0
- /package/src/transpiler/{output/codegen → logic/analysis}/helpers/__tests__/TransitiveModificationPropagator.test.ts +0 -0
package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import generateFunctionCall from "../CallExprGenerator";
|
|
3
3
|
import IGeneratorInput from "../../IGeneratorInput";
|
|
4
4
|
import IGeneratorState from "../../IGeneratorState";
|
|
5
5
|
import IOrchestrator from "../../IOrchestrator";
|
|
6
6
|
import * as Parser from "../../../../../logic/parser/grammar/CNextParser";
|
|
7
7
|
import ESymbolKind from "../../../../../../utils/types/ESymbolKind";
|
|
8
|
+
import CodeGenState from "../../../../../state/CodeGenState";
|
|
9
|
+
import TTypeInfo from "../../../types/TTypeInfo";
|
|
8
10
|
|
|
9
11
|
// ========================================================================
|
|
10
12
|
// Test Helpers
|
|
@@ -27,10 +29,18 @@ function createMockArgListContext(
|
|
|
27
29
|
function createMockInput(
|
|
28
30
|
overrides: Partial<IGeneratorInput> = {},
|
|
29
31
|
): IGeneratorInput {
|
|
32
|
+
// Also populate CodeGenState with the type registry entries
|
|
33
|
+
// This is needed because CallExprGenerator now uses CodeGenState directly
|
|
34
|
+
const typeRegistry =
|
|
35
|
+
(overrides.typeRegistry as Map<string, TTypeInfo>) ?? new Map();
|
|
36
|
+
for (const [name, info] of typeRegistry) {
|
|
37
|
+
CodeGenState.setVariableTypeInfo(name, info);
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
return {
|
|
31
41
|
symbols: null,
|
|
32
42
|
symbolTable: null,
|
|
33
|
-
typeRegistry
|
|
43
|
+
typeRegistry,
|
|
34
44
|
functionSignatures: new Map(),
|
|
35
45
|
knownFunctions: new Set(),
|
|
36
46
|
knownStructs: new Set(),
|
|
@@ -94,6 +104,11 @@ function createMockOrchestrator(
|
|
|
94
104
|
// ========================================================================
|
|
95
105
|
|
|
96
106
|
describe("CallExprGenerator", () => {
|
|
107
|
+
// Reset CodeGenState before each test to avoid state pollution
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
CodeGenState.reset();
|
|
110
|
+
});
|
|
111
|
+
|
|
97
112
|
describe("empty function call", () => {
|
|
98
113
|
it("generates call with no arguments when argCtx is null", () => {
|
|
99
114
|
const input = createMockInput();
|
|
@@ -756,7 +771,7 @@ describe("CallExprGenerator", () => {
|
|
|
756
771
|
});
|
|
757
772
|
|
|
758
773
|
describe("cross-file function calls (Issue #315)", () => {
|
|
759
|
-
it("looks up parameter info from SymbolTable
|
|
774
|
+
it("looks up parameter info from SymbolTable and passes primitives by value (Issue #786)", () => {
|
|
760
775
|
const argExprs = [createMockExpressionContext("myVal")];
|
|
761
776
|
const argCtx = createMockArgListContext(argExprs);
|
|
762
777
|
const symbolTable = {
|
|
@@ -777,6 +792,7 @@ describe("CallExprGenerator", () => {
|
|
|
777
792
|
isCNextFunction: vi.fn(() => true),
|
|
778
793
|
isFloatType: vi.fn(() => false),
|
|
779
794
|
isParameterPassByValue: vi.fn(() => false),
|
|
795
|
+
isStructType: vi.fn(() => false),
|
|
780
796
|
});
|
|
781
797
|
|
|
782
798
|
const result = generateFunctionCall(
|
|
@@ -788,7 +804,8 @@ describe("CallExprGenerator", () => {
|
|
|
788
804
|
);
|
|
789
805
|
|
|
790
806
|
expect(symbolTable.getOverloads).toHaveBeenCalledWith("crossFileFunc");
|
|
791
|
-
|
|
807
|
+
// Issue #786: Primitive types like u32 are now passed by value for cross-file calls
|
|
808
|
+
expect(result.code).toBe("crossFileFunc(myVal)");
|
|
792
809
|
});
|
|
793
810
|
|
|
794
811
|
it("passes small primitive by value for cross-file functions", () => {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Property access (.length, .capacity, .size)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { describe, it, expect, vi } from "vitest";
|
|
12
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
13
13
|
import generatePostfixExpression from "../PostfixExpressionGenerator";
|
|
14
14
|
import type IGeneratorInput from "../../IGeneratorInput";
|
|
15
15
|
import type IGeneratorState from "../../IGeneratorState";
|
|
@@ -18,6 +18,7 @@ import type ICodeGenSymbols from "../../../../../types/ICodeGenSymbols";
|
|
|
18
18
|
import type TTypeInfo from "../../../types/TTypeInfo";
|
|
19
19
|
import type TParameterInfo from "../../../types/TParameterInfo";
|
|
20
20
|
import * as Parser from "../../../../../logic/parser/grammar/CNextParser";
|
|
21
|
+
import CodeGenState from "../../../../../state/CodeGenState";
|
|
21
22
|
|
|
22
23
|
// ========================================================================
|
|
23
24
|
// Test Helpers - Mock Symbols
|
|
@@ -64,10 +65,17 @@ function createMockInput(overrides?: {
|
|
|
64
65
|
symbols?: ICodeGenSymbols;
|
|
65
66
|
typeRegistry?: Map<string, TTypeInfo>;
|
|
66
67
|
}): IGeneratorInput {
|
|
68
|
+
// Also populate CodeGenState with the type registry entries
|
|
69
|
+
// This is needed because PostfixExpressionGenerator now uses CodeGenState directly
|
|
70
|
+
const typeRegistry = overrides?.typeRegistry ?? new Map<string, TTypeInfo>();
|
|
71
|
+
for (const [name, info] of typeRegistry) {
|
|
72
|
+
CodeGenState.setVariableTypeInfo(name, info);
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
return {
|
|
68
76
|
symbolTable: null,
|
|
69
77
|
symbols: overrides?.symbols ?? createMockSymbols(),
|
|
70
|
-
typeRegistry
|
|
78
|
+
typeRegistry,
|
|
71
79
|
functionSignatures: new Map(),
|
|
72
80
|
knownFunctions: new Set(),
|
|
73
81
|
knownStructs: new Set(),
|
|
@@ -290,6 +298,11 @@ function createMockPostfixExpressionContext(
|
|
|
290
298
|
// ========================================================================
|
|
291
299
|
|
|
292
300
|
describe("PostfixExpressionGenerator", () => {
|
|
301
|
+
// Reset CodeGenState before each test to avoid state pollution
|
|
302
|
+
beforeEach(() => {
|
|
303
|
+
CodeGenState.reset();
|
|
304
|
+
});
|
|
305
|
+
|
|
293
306
|
describe("basic expression generation", () => {
|
|
294
307
|
it("generates simple identifier", () => {
|
|
295
308
|
const ctx = createMockPostfixExpressionContext("x", []);
|
|
@@ -134,9 +134,10 @@ class ArrayInitHelper {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
// Update type registry with inferred size for .length support
|
|
137
|
-
const existingType = CodeGenState.
|
|
137
|
+
const existingType = CodeGenState.getVariableTypeInfo(name);
|
|
138
138
|
if (existingType) {
|
|
139
139
|
existingType.arrayDimensions = [CodeGenState.lastArrayInitCount];
|
|
140
|
+
CodeGenState.setVariableTypeInfo(name, existingType);
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
return `[${CodeGenState.lastArrayInitCount}]`;
|
|
@@ -77,7 +77,7 @@ class AssignmentExpectedTypeResolver {
|
|
|
77
77
|
* Resolve expected type for a simple identifier target.
|
|
78
78
|
*/
|
|
79
79
|
private static resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
|
|
80
|
-
const typeInfo = CodeGenState.
|
|
80
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(id);
|
|
81
81
|
if (!typeInfo) {
|
|
82
82
|
return { expectedType: null, assignmentContext: null };
|
|
83
83
|
}
|
|
@@ -107,7 +107,7 @@ class AssignmentExpectedTypeResolver {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
const rootName = identifiers[0];
|
|
110
|
-
const rootTypeInfo = CodeGenState.
|
|
110
|
+
const rootTypeInfo = CodeGenState.getVariableTypeInfo(rootName);
|
|
111
111
|
|
|
112
112
|
if (!rootTypeInfo || !CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
|
|
113
113
|
return { expectedType: null, assignmentContext: null };
|
|
@@ -120,7 +120,7 @@ class AssignmentValidator {
|
|
|
120
120
|
const shadowName = `__bits_${id}`;
|
|
121
121
|
CodeGenState.floatShadowCurrent.delete(shadowName);
|
|
122
122
|
|
|
123
|
-
const targetTypeInfo = CodeGenState.
|
|
123
|
+
const targetTypeInfo = CodeGenState.getVariableTypeInfo(id);
|
|
124
124
|
if (!targetTypeInfo) {
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
@@ -172,7 +172,7 @@ class AssignmentValidator {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
// ADR-036: Compile-time bounds checking
|
|
175
|
-
const typeInfo = CodeGenState.
|
|
175
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(arrayName);
|
|
176
176
|
if (typeInfo?.isArray && typeInfo.arrayDimensions) {
|
|
177
177
|
TypeValidator.checkArrayBounds(
|
|
178
178
|
arrayName,
|
|
@@ -217,7 +217,7 @@ class AssignmentValidator {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
// ADR-029: Validate callback field assignments with nominal typing
|
|
220
|
-
const rootTypeInfo = CodeGenState.
|
|
220
|
+
const rootTypeInfo = CodeGenState.getVariableTypeInfo(rootName);
|
|
221
221
|
if (rootTypeInfo && CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
|
|
222
222
|
const structType = rootTypeInfo.baseType;
|
|
223
223
|
const callbackFieldKey = `${structType}.${memberName}`;
|
|
@@ -78,7 +78,7 @@ class EnumAssignmentValidator {
|
|
|
78
78
|
|
|
79
79
|
// Check if it's a variable of primitive integer type
|
|
80
80
|
if (/^[a-zA-Z_]\w*$/.exec(text)) {
|
|
81
|
-
const typeInfo = CodeGenState.
|
|
81
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(text);
|
|
82
82
|
if (
|
|
83
83
|
typeInfo &&
|
|
84
84
|
!typeInfo.isEnum &&
|
|
@@ -325,7 +325,7 @@ class StringDeclHelper {
|
|
|
325
325
|
const arraySize = CodeGenState.lastArrayInitCount;
|
|
326
326
|
|
|
327
327
|
// Update type registry with inferred size
|
|
328
|
-
CodeGenState.
|
|
328
|
+
CodeGenState.setVariableTypeInfo(name, {
|
|
329
329
|
baseType: "char",
|
|
330
330
|
bitWidth: 8,
|
|
331
331
|
isArray: true,
|
|
@@ -531,7 +531,7 @@ class StringDeclHelper {
|
|
|
531
531
|
callbacks.requireStringInclude();
|
|
532
532
|
|
|
533
533
|
// Register in type registry with inferred capacity
|
|
534
|
-
CodeGenState.
|
|
534
|
+
CodeGenState.setVariableTypeInfo(name, {
|
|
535
535
|
baseType: "char",
|
|
536
536
|
bitWidth: 8,
|
|
537
537
|
isArray: true,
|
|
@@ -56,7 +56,7 @@ describe("ArrayInitHelper", () => {
|
|
|
56
56
|
|
|
57
57
|
it("handles size inference with array initializer", () => {
|
|
58
58
|
// Add existing type to registry
|
|
59
|
-
CodeGenState.
|
|
59
|
+
CodeGenState.setVariableTypeInfo("arr", {
|
|
60
60
|
baseType: "u8",
|
|
61
61
|
bitWidth: 8,
|
|
62
62
|
isArray: true,
|
package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts
CHANGED
|
@@ -68,7 +68,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
68
68
|
describe("resolve()", () => {
|
|
69
69
|
describe("simple identifier", () => {
|
|
70
70
|
it("should resolve expected type for known variable", () => {
|
|
71
|
-
CodeGenState.
|
|
71
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
72
72
|
baseType: "u32",
|
|
73
73
|
bitWidth: 32,
|
|
74
74
|
isArray: false,
|
|
@@ -87,7 +87,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
it("should use specified overflow behavior", () => {
|
|
90
|
-
CodeGenState.
|
|
90
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
91
91
|
baseType: "u8",
|
|
92
92
|
bitWidth: 8,
|
|
93
93
|
isArray: false,
|
|
@@ -113,7 +113,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
113
113
|
|
|
114
114
|
describe("member access", () => {
|
|
115
115
|
it("should resolve expected type for struct field", () => {
|
|
116
|
-
CodeGenState.
|
|
116
|
+
CodeGenState.setVariableTypeInfo("config", {
|
|
117
117
|
baseType: "Config",
|
|
118
118
|
bitWidth: 0,
|
|
119
119
|
isArray: false,
|
|
@@ -128,7 +128,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it("should walk nested struct chain", () => {
|
|
131
|
-
CodeGenState.
|
|
131
|
+
CodeGenState.setVariableTypeInfo("app", {
|
|
132
132
|
baseType: "App",
|
|
133
133
|
bitWidth: 0,
|
|
134
134
|
isArray: false,
|
|
@@ -144,7 +144,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
it("should return null for non-struct root", () => {
|
|
147
|
-
CodeGenState.
|
|
147
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
148
148
|
baseType: "u32",
|
|
149
149
|
bitWidth: 32,
|
|
150
150
|
isArray: false,
|
|
@@ -158,7 +158,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
it("should return null for unknown field", () => {
|
|
161
|
-
CodeGenState.
|
|
161
|
+
CodeGenState.setVariableTypeInfo("config", {
|
|
162
162
|
baseType: "Config",
|
|
163
163
|
bitWidth: 0,
|
|
164
164
|
isArray: false,
|
|
@@ -175,7 +175,7 @@ describe("AssignmentExpectedTypeResolver", () => {
|
|
|
175
175
|
|
|
176
176
|
describe("array access", () => {
|
|
177
177
|
it("should return null for array access target", () => {
|
|
178
|
-
CodeGenState.
|
|
178
|
+
CodeGenState.setVariableTypeInfo("arr", {
|
|
179
179
|
baseType: "u32",
|
|
180
180
|
bitWidth: 32,
|
|
181
181
|
isArray: true,
|
|
@@ -141,7 +141,7 @@ describe("AssignmentValidator", () => {
|
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
it("should validate enum assignment for enum-typed variable", () => {
|
|
144
|
-
CodeGenState.
|
|
144
|
+
CodeGenState.setVariableTypeInfo("status", {
|
|
145
145
|
baseType: "Status",
|
|
146
146
|
bitWidth: 8,
|
|
147
147
|
isArray: false,
|
|
@@ -165,7 +165,7 @@ describe("AssignmentValidator", () => {
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
it("should validate integer assignment for integer-typed variable", () => {
|
|
168
|
-
CodeGenState.
|
|
168
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
169
169
|
baseType: "u32",
|
|
170
170
|
bitWidth: 32,
|
|
171
171
|
isArray: false,
|
|
@@ -190,7 +190,7 @@ describe("AssignmentValidator", () => {
|
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
it("should pass isCompound flag to integer validation", () => {
|
|
193
|
-
CodeGenState.
|
|
193
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
194
194
|
baseType: "u32",
|
|
195
195
|
bitWidth: 32,
|
|
196
196
|
isArray: false,
|
|
@@ -215,7 +215,7 @@ describe("AssignmentValidator", () => {
|
|
|
215
215
|
});
|
|
216
216
|
|
|
217
217
|
it("should rethrow validation error with line:column prefix", () => {
|
|
218
|
-
CodeGenState.
|
|
218
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
219
219
|
baseType: "u8",
|
|
220
220
|
bitWidth: 8,
|
|
221
221
|
isArray: false,
|
|
@@ -240,7 +240,7 @@ describe("AssignmentValidator", () => {
|
|
|
240
240
|
});
|
|
241
241
|
|
|
242
242
|
it("should handle non-Error validation exceptions", () => {
|
|
243
|
-
CodeGenState.
|
|
243
|
+
CodeGenState.setVariableTypeInfo("counter", {
|
|
244
244
|
baseType: "u8",
|
|
245
245
|
bitWidth: 8,
|
|
246
246
|
isArray: false,
|
|
@@ -298,7 +298,7 @@ describe("AssignmentValidator", () => {
|
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
it("should check array bounds for array with dimensions", () => {
|
|
301
|
-
CodeGenState.
|
|
301
|
+
CodeGenState.setVariableTypeInfo("arr", {
|
|
302
302
|
baseType: "u8",
|
|
303
303
|
bitWidth: 8,
|
|
304
304
|
isConst: false,
|
|
@@ -374,7 +374,7 @@ describe("AssignmentValidator", () => {
|
|
|
374
374
|
});
|
|
375
375
|
|
|
376
376
|
it("should validate callback assignment for callback field", () => {
|
|
377
|
-
CodeGenState.
|
|
377
|
+
CodeGenState.setVariableTypeInfo("handler", {
|
|
378
378
|
baseType: "Handler",
|
|
379
379
|
bitWidth: 0,
|
|
380
380
|
isArray: false,
|
|
@@ -275,7 +275,7 @@ describe("EnumAssignmentValidator", () => {
|
|
|
275
275
|
});
|
|
276
276
|
|
|
277
277
|
it("returns true for integer-typed variable", () => {
|
|
278
|
-
CodeGenState.
|
|
278
|
+
CodeGenState.setVariableTypeInfo("count", {
|
|
279
279
|
baseType: "u32",
|
|
280
280
|
bitWidth: 32,
|
|
281
281
|
isArray: false,
|
|
@@ -290,7 +290,7 @@ describe("EnumAssignmentValidator", () => {
|
|
|
290
290
|
});
|
|
291
291
|
|
|
292
292
|
it("returns false for enum-typed variable", () => {
|
|
293
|
-
CodeGenState.
|
|
293
|
+
CodeGenState.setVariableTypeInfo("state", {
|
|
294
294
|
baseType: "State",
|
|
295
295
|
bitWidth: 0,
|
|
296
296
|
isArray: false,
|
|
@@ -246,7 +246,7 @@ describe("StringDeclHelper", () => {
|
|
|
246
246
|
expect(result.handled).toBe(true);
|
|
247
247
|
expect(result.code).toBe('const char greeting[6] = "Hello";');
|
|
248
248
|
expect(requireStringInclude).toHaveBeenCalled();
|
|
249
|
-
expect(CodeGenState.
|
|
249
|
+
expect(CodeGenState.getVariableTypeInfo("greeting")).toMatchObject({
|
|
250
250
|
baseType: "char",
|
|
251
251
|
isString: true,
|
|
252
252
|
stringCapacity: 5,
|
|
@@ -821,9 +821,9 @@ describe("StringDeclHelper", () => {
|
|
|
821
821
|
expect(result.handled).toBe(true);
|
|
822
822
|
expect(result.code).toContain("[2]"); // Inferred size
|
|
823
823
|
expect(result.code).toContain("[11]"); // capacity + 1
|
|
824
|
-
expect(
|
|
825
|
-
|
|
826
|
-
]);
|
|
824
|
+
expect(
|
|
825
|
+
CodeGenState.getVariableTypeInfo("labels")?.arrayDimensions,
|
|
826
|
+
).toEqual([2, 11]);
|
|
827
827
|
});
|
|
828
828
|
|
|
829
829
|
it("generates string array without initializer (zero-init)", () => {
|
|
@@ -41,7 +41,7 @@ export default class EnumTypeResolver {
|
|
|
41
41
|
|
|
42
42
|
// Check if it's a simple identifier that's an enum variable
|
|
43
43
|
if (/^[a-zA-Z_]\w*$/.exec(text)) {
|
|
44
|
-
const typeInfo = CodeGenState.
|
|
44
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(text);
|
|
45
45
|
if (typeInfo?.isEnum && typeInfo.enumTypeName) {
|
|
46
46
|
return typeInfo.enumTypeName;
|
|
47
47
|
}
|
|
@@ -146,7 +146,7 @@ export default class EnumTypeResolver {
|
|
|
146
146
|
}
|
|
147
147
|
const varName = parts[1];
|
|
148
148
|
const scopedVarName = `${CodeGenState.currentScope}_${varName}`;
|
|
149
|
-
const typeInfo = CodeGenState.
|
|
149
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(scopedVarName);
|
|
150
150
|
if (typeInfo?.isEnum && typeInfo.enumTypeName) {
|
|
151
151
|
return typeInfo.enumTypeName;
|
|
152
152
|
}
|
|
@@ -121,7 +121,7 @@ describe("EnumTypeResolver", () => {
|
|
|
121
121
|
CodeGenState.symbols = createMockSymbols({
|
|
122
122
|
knownEnums: new Set(["State"]),
|
|
123
123
|
});
|
|
124
|
-
CodeGenState.
|
|
124
|
+
CodeGenState.setVariableTypeInfo("currentState", {
|
|
125
125
|
baseType: "State",
|
|
126
126
|
bitWidth: 0,
|
|
127
127
|
isArray: false,
|
|
@@ -135,7 +135,7 @@ describe("EnumTypeResolver", () => {
|
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
it("returns null for non-enum variable", () => {
|
|
138
|
-
CodeGenState.
|
|
138
|
+
CodeGenState.setVariableTypeInfo("count", {
|
|
139
139
|
baseType: "u32",
|
|
140
140
|
bitWidth: 32,
|
|
141
141
|
isArray: false,
|
|
@@ -190,7 +190,7 @@ describe("EnumTypeResolver", () => {
|
|
|
190
190
|
CodeGenState.symbols = createMockSymbols({
|
|
191
191
|
knownEnums: new Set(["Motor_State"]),
|
|
192
192
|
});
|
|
193
|
-
CodeGenState.
|
|
193
|
+
CodeGenState.setVariableTypeInfo("Motor_current", {
|
|
194
194
|
baseType: "Motor_State",
|
|
195
195
|
bitWidth: 0,
|
|
196
196
|
isArray: false,
|
|
@@ -261,7 +261,7 @@ describe("EnumTypeResolver", () => {
|
|
|
261
261
|
CodeGenState.symbols = createMockSymbols({
|
|
262
262
|
knownEnums: new Set(["EValueId"]),
|
|
263
263
|
});
|
|
264
|
-
CodeGenState.
|
|
264
|
+
CodeGenState.setVariableTypeInfo("input", {
|
|
265
265
|
baseType: "TInput",
|
|
266
266
|
bitWidth: 0,
|
|
267
267
|
isArray: false,
|
|
@@ -280,7 +280,7 @@ describe("EnumTypeResolver", () => {
|
|
|
280
280
|
symbolTable.addStructField("TInput", "count", "u32");
|
|
281
281
|
CodeGenState.symbolTable = symbolTable;
|
|
282
282
|
CodeGenState.symbols = createMockSymbols();
|
|
283
|
-
CodeGenState.
|
|
283
|
+
CodeGenState.setVariableTypeInfo("input", {
|
|
284
284
|
baseType: "TInput",
|
|
285
285
|
bitWidth: 0,
|
|
286
286
|
isArray: false,
|
|
@@ -30,6 +30,8 @@ import ICallbackTypeInfo from "../output/codegen/types/ICallbackTypeInfo";
|
|
|
30
30
|
import ITargetCapabilities from "../output/codegen/types/ITargetCapabilities";
|
|
31
31
|
import TOverflowBehavior from "../output/codegen/types/TOverflowBehavior";
|
|
32
32
|
import TYPE_WIDTH from "../output/codegen/types/TYPE_WIDTH";
|
|
33
|
+
import ESymbolKind from "../../utils/types/ESymbolKind";
|
|
34
|
+
import ESourceLanguage from "../../utils/types/ESourceLanguage";
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* Default target capabilities (safe fallback)
|
|
@@ -101,8 +103,12 @@ export default class CodeGenState {
|
|
|
101
103
|
// TYPE TRACKING
|
|
102
104
|
// ===========================================================================
|
|
103
105
|
|
|
104
|
-
/**
|
|
105
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Track variable types for bit access, .length, and type inference.
|
|
108
|
+
* PRIVATE: Use getVariableTypeInfo()/setVariableTypeInfo() instead.
|
|
109
|
+
* This ensures cross-file variables from SymbolTable are also found.
|
|
110
|
+
*/
|
|
111
|
+
private static typeRegistry: Map<string, TTypeInfo> = new Map();
|
|
106
112
|
|
|
107
113
|
/** Bug #8: Compile-time const values for array size resolution */
|
|
108
114
|
static constValues: Map<string, number> = new Map();
|
|
@@ -451,9 +457,121 @@ export default class CodeGenState {
|
|
|
451
457
|
|
|
452
458
|
/**
|
|
453
459
|
* Get type info for a variable.
|
|
460
|
+
* Checks local typeRegistry first, then falls back to SymbolTable
|
|
461
|
+
* for cross-file variables from included .cnx files.
|
|
462
|
+
*
|
|
463
|
+
* Issue #786: This unified lookup ensures cross-file variables
|
|
464
|
+
* (defined in included files) are found even before code generation
|
|
465
|
+
* registers them locally.
|
|
466
|
+
*/
|
|
467
|
+
static getVariableTypeInfo(name: string): TTypeInfo | undefined {
|
|
468
|
+
// First check the local type registry (current file's variables)
|
|
469
|
+
const localInfo = this.typeRegistry.get(name);
|
|
470
|
+
if (localInfo) {
|
|
471
|
+
return localInfo;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Fall back to SymbolTable for cross-file C-Next variables only.
|
|
475
|
+
// C/C++ header symbols don't have complete type info (e.g., isArray),
|
|
476
|
+
// so we only use C-Next symbols from SymbolTable.
|
|
477
|
+
const symbol = this.symbolTable.getSymbol(name);
|
|
478
|
+
if (
|
|
479
|
+
symbol?.kind === ESymbolKind.Variable &&
|
|
480
|
+
symbol.type &&
|
|
481
|
+
symbol.sourceLanguage === ESourceLanguage.CNext
|
|
482
|
+
) {
|
|
483
|
+
return this.convertSymbolToTypeInfo(symbol);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return undefined;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Legacy alias for getVariableTypeInfo.
|
|
491
|
+
* @deprecated Use getVariableTypeInfo() instead
|
|
454
492
|
*/
|
|
455
493
|
static getTypeInfo(name: string): TTypeInfo | undefined {
|
|
456
|
-
return this.
|
|
494
|
+
return this.getVariableTypeInfo(name);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Check if a variable type is registered (locally or in SymbolTable).
|
|
499
|
+
*/
|
|
500
|
+
static hasVariableTypeInfo(name: string): boolean {
|
|
501
|
+
if (this.typeRegistry.has(name)) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
const symbol = this.symbolTable.getSymbol(name);
|
|
505
|
+
return (
|
|
506
|
+
symbol?.kind === ESymbolKind.Variable &&
|
|
507
|
+
symbol.type !== undefined &&
|
|
508
|
+
symbol.sourceLanguage === ESourceLanguage.CNext
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Set variable type info in the local registry.
|
|
514
|
+
*/
|
|
515
|
+
static setVariableTypeInfo(name: string, info: TTypeInfo): void {
|
|
516
|
+
this.typeRegistry.set(name, info);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Delete variable type info from the local registry.
|
|
521
|
+
*/
|
|
522
|
+
static deleteVariableTypeInfo(name: string): void {
|
|
523
|
+
this.typeRegistry.delete(name);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Get a read-only view of the local type registry.
|
|
528
|
+
* Used for passing to helper functions that need to iterate over types.
|
|
529
|
+
* Note: This only returns locally registered types, not cross-file symbols.
|
|
530
|
+
*/
|
|
531
|
+
static getTypeRegistryView(): ReadonlyMap<string, TTypeInfo> {
|
|
532
|
+
return this.typeRegistry;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Convert an ISymbol to TTypeInfo for unified type lookups.
|
|
537
|
+
* Used when looking up cross-file variables from SymbolTable.
|
|
538
|
+
*/
|
|
539
|
+
private static convertSymbolToTypeInfo(symbol: {
|
|
540
|
+
type?: string;
|
|
541
|
+
isArray?: boolean;
|
|
542
|
+
arrayDimensions?: string[];
|
|
543
|
+
isConst?: boolean;
|
|
544
|
+
isAtomic?: boolean;
|
|
545
|
+
}): TTypeInfo {
|
|
546
|
+
const rawType = symbol.type || "unknown";
|
|
547
|
+
|
|
548
|
+
// Parse string<N> type pattern
|
|
549
|
+
const stringPattern = /^string<(\d+)>$/;
|
|
550
|
+
const stringMatch = stringPattern.exec(rawType);
|
|
551
|
+
const isString = stringMatch !== null;
|
|
552
|
+
const stringCapacity = stringMatch
|
|
553
|
+
? Number.parseInt(stringMatch[1], 10)
|
|
554
|
+
: undefined;
|
|
555
|
+
// Use "char" for string types to match local convention (StringDeclHelper.ts)
|
|
556
|
+
const baseType = isString ? "char" : rawType;
|
|
557
|
+
|
|
558
|
+
const isEnum = this.isKnownEnum(baseType);
|
|
559
|
+
|
|
560
|
+
return {
|
|
561
|
+
baseType,
|
|
562
|
+
// Use bitWidth 8 for strings (char), otherwise lookup from TYPE_WIDTH
|
|
563
|
+
bitWidth: isString ? 8 : TYPE_WIDTH[baseType] || 0,
|
|
564
|
+
isArray: symbol.isArray || false,
|
|
565
|
+
arrayDimensions: symbol.arrayDimensions
|
|
566
|
+
?.map((d) => Number.parseInt(d, 10))
|
|
567
|
+
.filter((n) => !Number.isNaN(n)),
|
|
568
|
+
isConst: symbol.isConst || false,
|
|
569
|
+
isAtomic: symbol.isAtomic || false,
|
|
570
|
+
isEnum,
|
|
571
|
+
enumTypeName: isEnum ? baseType : undefined,
|
|
572
|
+
isString,
|
|
573
|
+
stringCapacity,
|
|
574
|
+
};
|
|
457
575
|
}
|
|
458
576
|
|
|
459
577
|
/**
|
|
@@ -785,7 +903,7 @@ export default class CodeGenState {
|
|
|
785
903
|
* Register a variable type.
|
|
786
904
|
*/
|
|
787
905
|
static registerType(name: string, info: TTypeInfo): void {
|
|
788
|
-
this.
|
|
906
|
+
this.setVariableTypeInfo(name, info);
|
|
789
907
|
}
|
|
790
908
|
|
|
791
909
|
/**
|