c-next 0.1.68 → 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 +160 -742
- 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 +7 -7
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +29 -1
- 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 +4 -6
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +4 -2
- 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/AssignmentContextBuilder.ts +49 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +15 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +30 -17
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +25 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +19 -8
- 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__/AccessPatternHandlers.test.ts +9 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +41 -26
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +29 -37
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +27 -19
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +10 -1
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/SpecialHandlers.test.ts +51 -33
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +9 -1
- 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/MemberSeparatorResolver.ts +6 -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 +157 -5
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +274 -6
- /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
|
@@ -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 &&
|
|
@@ -89,7 +89,12 @@ class MemberSeparatorResolver {
|
|
|
89
89
|
// Scope member access: Sensor.buffer -> Sensor_buffer
|
|
90
90
|
// Works with or without global. prefix (both are valid syntax)
|
|
91
91
|
if (deps.isKnownScope(identifierChain[0])) {
|
|
92
|
-
|
|
92
|
+
// Issue #779: Skip cross-scope validation for scoped register access
|
|
93
|
+
// Board.GPIO where Board_GPIO is a known register is valid
|
|
94
|
+
const scopedRegisterName = `${identifierChain[0]}_${memberName}`;
|
|
95
|
+
if (!deps.isKnownRegister(scopedRegisterName)) {
|
|
96
|
+
deps.validateCrossScopeVisibility(identifierChain[0], memberName);
|
|
97
|
+
}
|
|
93
98
|
return "_";
|
|
94
99
|
}
|
|
95
100
|
|
|
@@ -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();
|
|
@@ -187,7 +193,7 @@ export default class CodeGenState {
|
|
|
187
193
|
static localArrays: Set<string> = new Set();
|
|
188
194
|
|
|
189
195
|
/** Scope member names: scope -> Set of member names */
|
|
190
|
-
static scopeMembers: Map<string, Set<string>> = new Map();
|
|
196
|
+
private static scopeMembers: Map<string, Set<string>> = new Map();
|
|
191
197
|
|
|
192
198
|
/** Float bit indexing: declared shadow variables */
|
|
193
199
|
static floatBitShadows: Set<string> = new Set();
|
|
@@ -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
|
/**
|
|
@@ -463,6 +581,26 @@ export default class CodeGenState {
|
|
|
463
581
|
return this.modifiedParameters.get(funcName)?.has(paramName) ?? false;
|
|
464
582
|
}
|
|
465
583
|
|
|
584
|
+
/**
|
|
585
|
+
* Compute unmodified parameters for all functions on-demand.
|
|
586
|
+
* Returns a map of function name -> Set of parameter names NOT modified.
|
|
587
|
+
* Computed from functionSignatures and modifiedParameters (no cached state).
|
|
588
|
+
*/
|
|
589
|
+
static getUnmodifiedParameters(): Map<string, Set<string>> {
|
|
590
|
+
const result = new Map<string, Set<string>>();
|
|
591
|
+
for (const [funcName, signature] of this.functionSignatures) {
|
|
592
|
+
const modifiedSet = this.modifiedParameters.get(funcName);
|
|
593
|
+
const unmodified = new Set<string>();
|
|
594
|
+
for (const param of signature.parameters) {
|
|
595
|
+
if (!modifiedSet?.has(param.name)) {
|
|
596
|
+
unmodified.add(param.name);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
result.set(funcName, unmodified);
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
|
|
466
604
|
/**
|
|
467
605
|
* Check if a parameter should pass by value.
|
|
468
606
|
*/
|
|
@@ -542,6 +680,20 @@ export default class CodeGenState {
|
|
|
542
680
|
return this.scopeMembers.get(scopeName);
|
|
543
681
|
}
|
|
544
682
|
|
|
683
|
+
/**
|
|
684
|
+
* Set members of a scope.
|
|
685
|
+
*/
|
|
686
|
+
static setScopeMembers(scopeName: string, members: Set<string>): void {
|
|
687
|
+
this.scopeMembers.set(scopeName, members);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Get all scope members (for IGeneratorState).
|
|
692
|
+
*/
|
|
693
|
+
static getAllScopeMembers(): ReadonlyMap<string, ReadonlySet<string>> {
|
|
694
|
+
return this.scopeMembers;
|
|
695
|
+
}
|
|
696
|
+
|
|
545
697
|
/**
|
|
546
698
|
* Check if an identifier is a member of the current scope.
|
|
547
699
|
*/
|
|
@@ -751,7 +903,7 @@ export default class CodeGenState {
|
|
|
751
903
|
* Register a variable type.
|
|
752
904
|
*/
|
|
753
905
|
static registerType(name: string, info: TTypeInfo): void {
|
|
754
|
-
this.
|
|
906
|
+
this.setVariableTypeInfo(name, info);
|
|
755
907
|
}
|
|
756
908
|
|
|
757
909
|
/**
|