c-next 0.1.61 → 0.1.63
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/README.md +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- package/src/transpiler/types/ITranspileContribution.ts +0 -32
|
@@ -2,111 +2,200 @@
|
|
|
2
2
|
* Unit tests for MemberChainAnalyzer
|
|
3
3
|
*
|
|
4
4
|
* Issue #644: Tests for the extracted member chain analyzer.
|
|
5
|
+
* Updated to use unified postfixTargetOp grammar after consolidation.
|
|
6
|
+
* Migrated to use CodeGenState instead of constructor DI.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
|
-
import { describe, it, expect, beforeEach
|
|
9
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
8
10
|
import MemberChainAnalyzer from "../MemberChainAnalyzer.js";
|
|
9
|
-
import
|
|
11
|
+
import CodeGenState from "../../CodeGenState.js";
|
|
10
12
|
import type * as Parser from "../../../../logic/parser/grammar/CNextParser.js";
|
|
11
13
|
|
|
12
|
-
/**
|
|
13
|
-
interface
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
structFieldArrays: ReadonlyMap<string, ReadonlySet<string>>;
|
|
17
|
-
isKnownStruct: (name: string) => boolean;
|
|
18
|
-
generateExpression: (ctx: Parser.ExpressionContext) => string;
|
|
14
|
+
/** Mock type for PostfixTargetOpContext */
|
|
15
|
+
interface IMockPostfixOp {
|
|
16
|
+
IDENTIFIER: () => { getText: () => string } | null;
|
|
17
|
+
expression: () => { getText: () => string }[];
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Create a mock PostfixTargetOpContext for member access: .memberName
|
|
22
|
+
*/
|
|
23
|
+
function createMemberOp(memberName: string): IMockPostfixOp {
|
|
24
|
+
return {
|
|
25
|
+
IDENTIFIER: () => ({ getText: () => memberName }),
|
|
26
|
+
expression: () => [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a mock PostfixTargetOpContext for subscript access: [expr]
|
|
32
|
+
*/
|
|
33
|
+
function createSubscriptOp(exprValue: string): IMockPostfixOp {
|
|
34
|
+
return {
|
|
35
|
+
IDENTIFIER: () => null,
|
|
36
|
+
expression: () => [{ getText: () => exprValue }],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a mock PostfixTargetOpContext for bit range access: [start, width]
|
|
42
|
+
*/
|
|
43
|
+
function createBitRangeOp(start: string, width: string): IMockPostfixOp {
|
|
44
|
+
return {
|
|
45
|
+
IDENTIFIER: () => null,
|
|
46
|
+
expression: () => [{ getText: () => start }, { getText: () => width }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
27
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Create a mock AssignmentTargetContext
|
|
52
|
+
*/
|
|
53
|
+
function createTargetCtx(baseId: string | null, postfixOps: IMockPostfixOp[]) {
|
|
54
|
+
return {
|
|
55
|
+
IDENTIFIER: () => (baseId ? { getText: () => baseId } : null),
|
|
56
|
+
postfixTargetOp: () => postfixOps,
|
|
57
|
+
} as unknown as Parser.AssignmentTargetContext;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Mock generateExpression callback - just returns getText() of the context
|
|
62
|
+
*/
|
|
63
|
+
function mockGenerateExpression(ctx: Parser.ExpressionContext): string {
|
|
64
|
+
return (ctx as unknown as { getText(): string }).getText();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe("MemberChainAnalyzer", () => {
|
|
28
68
|
beforeEach(() => {
|
|
29
|
-
|
|
30
|
-
structFields = new Map();
|
|
31
|
-
structFieldArrays = new Map();
|
|
32
|
-
|
|
33
|
-
deps = {
|
|
34
|
-
typeRegistry,
|
|
35
|
-
structFields,
|
|
36
|
-
structFieldArrays,
|
|
37
|
-
isKnownStruct: vi.fn((name) => structFields.has(name)),
|
|
38
|
-
generateExpression: vi.fn((ctx) => ctx.getText()),
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
analyzer = new MemberChainAnalyzer(deps);
|
|
69
|
+
CodeGenState.reset();
|
|
42
70
|
});
|
|
43
71
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Helper to set up struct fields in CodeGenState.symbols
|
|
74
|
+
*/
|
|
75
|
+
function setupStructFields(
|
|
76
|
+
structName: string,
|
|
77
|
+
fields: Map<string, string>,
|
|
78
|
+
arrayFields: Set<string> = new Set(),
|
|
79
|
+
): void {
|
|
80
|
+
// Initialize symbols if not set
|
|
81
|
+
if (!CodeGenState.symbols) {
|
|
82
|
+
CodeGenState.symbols = {
|
|
83
|
+
knownStructs: new Set(),
|
|
84
|
+
knownScopes: new Set(),
|
|
85
|
+
knownEnums: new Set(),
|
|
86
|
+
knownBitmaps: new Set(),
|
|
87
|
+
knownRegisters: new Set(),
|
|
88
|
+
structFields: new Map(),
|
|
89
|
+
structFieldArrays: new Map(),
|
|
90
|
+
structFieldDimensions: new Map(),
|
|
91
|
+
enumMembers: new Map(),
|
|
92
|
+
bitmapFields: new Map(),
|
|
93
|
+
bitmapBackingType: new Map(),
|
|
94
|
+
bitmapBitWidth: new Map(),
|
|
95
|
+
scopeMembers: new Map(),
|
|
96
|
+
scopeMemberVisibility: new Map(),
|
|
97
|
+
scopedRegisters: new Map(),
|
|
98
|
+
registerMemberAccess: new Map(),
|
|
99
|
+
registerMemberTypes: new Map(),
|
|
100
|
+
registerBaseAddresses: new Map(),
|
|
101
|
+
registerMemberOffsets: new Map(),
|
|
102
|
+
registerMemberCTypes: new Map(),
|
|
103
|
+
scopeVariableUsage: new Map(),
|
|
104
|
+
scopePrivateConstValues: new Map(),
|
|
105
|
+
functionReturnTypes: new Map(),
|
|
106
|
+
getSingleFunctionForVariable: () => null,
|
|
107
|
+
hasPublicSymbols: () => false,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
(CodeGenState.symbols.knownStructs as Set<string>).add(structName);
|
|
111
|
+
(CodeGenState.symbols.structFields as Map<string, Map<string, string>>).set(
|
|
112
|
+
structName,
|
|
113
|
+
fields,
|
|
114
|
+
);
|
|
115
|
+
(CodeGenState.symbols.structFieldArrays as Map<string, Set<string>>).set(
|
|
116
|
+
structName,
|
|
117
|
+
arrayFields,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
50
120
|
|
|
51
|
-
|
|
121
|
+
describe("analyze", () => {
|
|
122
|
+
it("returns isBitAccess false when no base identifier", () => {
|
|
123
|
+
const targetCtx = createTargetCtx(null, []);
|
|
124
|
+
const result = MemberChainAnalyzer.analyze(
|
|
125
|
+
targetCtx,
|
|
126
|
+
mockGenerateExpression,
|
|
127
|
+
);
|
|
128
|
+
expect(result.isBitAccess).toBe(false);
|
|
129
|
+
});
|
|
52
130
|
|
|
131
|
+
it("returns isBitAccess false when no postfix operations", () => {
|
|
132
|
+
const targetCtx = createTargetCtx("x", []);
|
|
133
|
+
const result = MemberChainAnalyzer.analyze(
|
|
134
|
+
targetCtx,
|
|
135
|
+
mockGenerateExpression,
|
|
136
|
+
);
|
|
53
137
|
expect(result.isBitAccess).toBe(false);
|
|
54
138
|
});
|
|
55
139
|
|
|
56
|
-
it("returns isBitAccess false when
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
140
|
+
it("returns isBitAccess false when last op is member access", () => {
|
|
141
|
+
// point.flags (no subscript at end)
|
|
142
|
+
CodeGenState.typeRegistry.set("point", {
|
|
143
|
+
baseType: "Point",
|
|
144
|
+
bitWidth: 0,
|
|
145
|
+
isArray: false,
|
|
146
|
+
isConst: false,
|
|
147
|
+
});
|
|
148
|
+
const pointFields = new Map<string, string>();
|
|
149
|
+
pointFields.set("flags", "u8");
|
|
150
|
+
setupStructFields("Point", pointFields);
|
|
62
151
|
|
|
63
|
-
const targetCtx =
|
|
64
|
-
|
|
65
|
-
|
|
152
|
+
const targetCtx = createTargetCtx("point", [createMemberOp("flags")]);
|
|
153
|
+
const result = MemberChainAnalyzer.analyze(
|
|
154
|
+
targetCtx,
|
|
155
|
+
mockGenerateExpression,
|
|
156
|
+
);
|
|
157
|
+
expect(result.isBitAccess).toBe(false);
|
|
158
|
+
});
|
|
66
159
|
|
|
67
|
-
|
|
160
|
+
it("returns isBitAccess false when last subscript has 2 expressions (bit range)", () => {
|
|
161
|
+
// flags[0, 8] - bit range, not single bit access
|
|
162
|
+
CodeGenState.typeRegistry.set("flags", {
|
|
163
|
+
baseType: "u32",
|
|
164
|
+
bitWidth: 32,
|
|
165
|
+
isArray: false,
|
|
166
|
+
isConst: false,
|
|
167
|
+
});
|
|
68
168
|
|
|
169
|
+
const targetCtx = createTargetCtx("flags", [createBitRangeOp("0", "8")]);
|
|
170
|
+
const result = MemberChainAnalyzer.analyze(
|
|
171
|
+
targetCtx,
|
|
172
|
+
mockGenerateExpression,
|
|
173
|
+
);
|
|
69
174
|
expect(result.isBitAccess).toBe(false);
|
|
70
175
|
});
|
|
71
176
|
|
|
72
|
-
it("detects bit access on struct member", () => {
|
|
177
|
+
it("detects bit access on struct member: point.flags[3]", () => {
|
|
73
178
|
// Setup: struct Point { u8 flags; }
|
|
74
179
|
const pointFields = new Map<string, string>();
|
|
75
180
|
pointFields.set("flags", "u8");
|
|
76
|
-
|
|
77
|
-
structFieldArrays.set("Point", new Set());
|
|
181
|
+
setupStructFields("Point", pointFields);
|
|
78
182
|
|
|
79
|
-
|
|
80
|
-
typeRegistry.set("point", {
|
|
183
|
+
CodeGenState.typeRegistry.set("point", {
|
|
81
184
|
baseType: "Point",
|
|
82
185
|
bitWidth: 0,
|
|
83
|
-
isConst: false,
|
|
84
186
|
isArray: false,
|
|
187
|
+
isConst: false,
|
|
85
188
|
});
|
|
86
189
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{ getText: () => "point" },
|
|
92
|
-
{ getText: () => "flags" },
|
|
93
|
-
],
|
|
94
|
-
expression: () => [mockExpr],
|
|
95
|
-
children: [
|
|
96
|
-
{ getText: () => "point" },
|
|
97
|
-
{ getText: () => "." },
|
|
98
|
-
{ getText: () => "flags" },
|
|
99
|
-
{ getText: () => "[" },
|
|
100
|
-
mockExpr,
|
|
101
|
-
{ getText: () => "]" },
|
|
102
|
-
],
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const targetCtx = {
|
|
106
|
-
memberAccess: () => memberAccessCtx,
|
|
107
|
-
} as never;
|
|
190
|
+
const targetCtx = createTargetCtx("point", [
|
|
191
|
+
createMemberOp("flags"),
|
|
192
|
+
createSubscriptOp("3"),
|
|
193
|
+
]);
|
|
108
194
|
|
|
109
|
-
const result =
|
|
195
|
+
const result = MemberChainAnalyzer.analyze(
|
|
196
|
+
targetCtx,
|
|
197
|
+
mockGenerateExpression,
|
|
198
|
+
);
|
|
110
199
|
|
|
111
200
|
expect(result.isBitAccess).toBe(true);
|
|
112
201
|
expect(result.baseTarget).toBe("point.flags");
|
|
@@ -114,254 +203,178 @@ describe("MemberChainAnalyzer", () => {
|
|
|
114
203
|
expect(result.baseType).toBe("u8");
|
|
115
204
|
});
|
|
116
205
|
|
|
117
|
-
it("returns
|
|
118
|
-
// Setup: struct
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
structFieldArrays.set("Data", new Set(["values"]));
|
|
206
|
+
it("returns false for subscript on array member: grid.items[0]", () => {
|
|
207
|
+
// Setup: struct Grid { u8 items[10]; }
|
|
208
|
+
const gridFields = new Map<string, string>();
|
|
209
|
+
gridFields.set("items", "u8");
|
|
210
|
+
setupStructFields("Grid", gridFields, new Set(["items"]));
|
|
123
211
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
baseType: "Data",
|
|
212
|
+
CodeGenState.typeRegistry.set("grid", {
|
|
213
|
+
baseType: "Grid",
|
|
127
214
|
bitWidth: 0,
|
|
128
|
-
isConst: false,
|
|
129
215
|
isArray: false,
|
|
216
|
+
isConst: false,
|
|
130
217
|
});
|
|
131
218
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{ getText: () => "data" },
|
|
137
|
-
{ getText: () => "values" },
|
|
138
|
-
],
|
|
139
|
-
expression: () => [mockExpr],
|
|
140
|
-
children: [
|
|
141
|
-
{ getText: () => "data" },
|
|
142
|
-
{ getText: () => "." },
|
|
143
|
-
{ getText: () => "values" },
|
|
144
|
-
{ getText: () => "[" },
|
|
145
|
-
mockExpr,
|
|
146
|
-
{ getText: () => "]" },
|
|
147
|
-
],
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const targetCtx = {
|
|
151
|
-
memberAccess: () => memberAccessCtx,
|
|
152
|
-
} as never;
|
|
219
|
+
const targetCtx = createTargetCtx("grid", [
|
|
220
|
+
createMemberOp("items"),
|
|
221
|
+
createSubscriptOp("0"),
|
|
222
|
+
]);
|
|
153
223
|
|
|
154
|
-
const result =
|
|
224
|
+
const result = MemberChainAnalyzer.analyze(
|
|
225
|
+
targetCtx,
|
|
226
|
+
mockGenerateExpression,
|
|
227
|
+
);
|
|
155
228
|
|
|
156
|
-
//
|
|
229
|
+
// items is an array, so [0] is array access, not bit access
|
|
157
230
|
expect(result.isBitAccess).toBe(false);
|
|
158
231
|
});
|
|
159
232
|
|
|
160
|
-
it("returns
|
|
161
|
-
// Setup: struct
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
structFieldArrays.set("Config", new Set());
|
|
233
|
+
it("returns false for non-integer member: point.name[0]", () => {
|
|
234
|
+
// Setup: struct Point { string name; }
|
|
235
|
+
const pointFields = new Map<string, string>();
|
|
236
|
+
pointFields.set("name", "string");
|
|
237
|
+
setupStructFields("Point", pointFields);
|
|
166
238
|
|
|
167
|
-
typeRegistry.set("
|
|
168
|
-
baseType: "
|
|
239
|
+
CodeGenState.typeRegistry.set("point", {
|
|
240
|
+
baseType: "Point",
|
|
169
241
|
bitWidth: 0,
|
|
170
|
-
isConst: false,
|
|
171
242
|
isArray: false,
|
|
243
|
+
isConst: false,
|
|
172
244
|
});
|
|
173
245
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
{ getText: () => "config" },
|
|
179
|
-
{ getText: () => "value" },
|
|
180
|
-
],
|
|
181
|
-
expression: () => [mockExpr],
|
|
182
|
-
children: [
|
|
183
|
-
{ getText: () => "config" },
|
|
184
|
-
{ getText: () => "." },
|
|
185
|
-
{ getText: () => "value" },
|
|
186
|
-
{ getText: () => "[" },
|
|
187
|
-
mockExpr,
|
|
188
|
-
{ getText: () => "]" },
|
|
189
|
-
],
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const targetCtx = {
|
|
193
|
-
memberAccess: () => memberAccessCtx,
|
|
194
|
-
} as never;
|
|
246
|
+
const targetCtx = createTargetCtx("point", [
|
|
247
|
+
createMemberOp("name"),
|
|
248
|
+
createSubscriptOp("0"),
|
|
249
|
+
]);
|
|
195
250
|
|
|
196
|
-
const result =
|
|
251
|
+
const result = MemberChainAnalyzer.analyze(
|
|
252
|
+
targetCtx,
|
|
253
|
+
mockGenerateExpression,
|
|
254
|
+
);
|
|
197
255
|
|
|
198
|
-
//
|
|
199
|
-
// The analyzer only detects bit access on integer types
|
|
256
|
+
// name is a string, not an integer, so no bit access
|
|
200
257
|
expect(result.isBitAccess).toBe(false);
|
|
201
258
|
});
|
|
202
259
|
|
|
203
|
-
it("
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const targetCtx = {
|
|
211
|
-
memberAccess: () => memberAccessCtx,
|
|
212
|
-
} as never;
|
|
213
|
-
|
|
214
|
-
const result = analyzer.analyze(targetCtx);
|
|
260
|
+
it("detects bit access through array-of-structs: devices[0].flags[7]", () => {
|
|
261
|
+
// Setup: struct Device { u8 flags; }, Device devices[4];
|
|
262
|
+
const deviceFields = new Map<string, string>();
|
|
263
|
+
deviceFields.set("flags", "u8");
|
|
264
|
+
setupStructFields("Device", deviceFields);
|
|
215
265
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
it("should track type through nested struct member chain", () => {
|
|
220
|
-
// Setup: struct Inner { u8 flags; }, struct Outer { Inner child; }
|
|
221
|
-
const innerFields = new Map<string, string>();
|
|
222
|
-
innerFields.set("flags", "u8");
|
|
223
|
-
structFields.set("Inner", innerFields);
|
|
224
|
-
structFieldArrays.set("Inner", new Set());
|
|
225
|
-
|
|
226
|
-
const outerFields = new Map<string, string>();
|
|
227
|
-
outerFields.set("child", "Inner");
|
|
228
|
-
structFields.set("Outer", outerFields);
|
|
229
|
-
structFieldArrays.set("Outer", new Set());
|
|
230
|
-
|
|
231
|
-
// Variable 'obj' is of type Outer
|
|
232
|
-
typeRegistry.set("obj", {
|
|
233
|
-
baseType: "Outer",
|
|
266
|
+
CodeGenState.typeRegistry.set("devices", {
|
|
267
|
+
baseType: "Device",
|
|
234
268
|
bitWidth: 0,
|
|
269
|
+
isArray: true,
|
|
235
270
|
isConst: false,
|
|
236
|
-
|
|
271
|
+
arrayDimensions: [4],
|
|
237
272
|
});
|
|
238
273
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
{ getText: () => "child" },
|
|
245
|
-
{ getText: () => "flags" },
|
|
246
|
-
],
|
|
247
|
-
expression: () => [mockExpr],
|
|
248
|
-
children: [
|
|
249
|
-
{ getText: () => "obj" },
|
|
250
|
-
{ getText: () => "." },
|
|
251
|
-
{ getText: () => "child" },
|
|
252
|
-
{ getText: () => "." },
|
|
253
|
-
{ getText: () => "flags" },
|
|
254
|
-
{ getText: () => "[" },
|
|
255
|
-
mockExpr,
|
|
256
|
-
{ getText: () => "]" },
|
|
257
|
-
],
|
|
258
|
-
};
|
|
274
|
+
const targetCtx = createTargetCtx("devices", [
|
|
275
|
+
createSubscriptOp("0"),
|
|
276
|
+
createMemberOp("flags"),
|
|
277
|
+
createSubscriptOp("7"),
|
|
278
|
+
]);
|
|
259
279
|
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const result = analyzer.analyze(targetCtx);
|
|
280
|
+
const result = MemberChainAnalyzer.analyze(
|
|
281
|
+
targetCtx,
|
|
282
|
+
mockGenerateExpression,
|
|
283
|
+
);
|
|
265
284
|
|
|
266
285
|
expect(result.isBitAccess).toBe(true);
|
|
267
|
-
expect(result.baseTarget).toBe("
|
|
268
|
-
expect(result.bitIndex).toBe("
|
|
286
|
+
expect(result.baseTarget).toBe("devices[0].flags");
|
|
287
|
+
expect(result.bitIndex).toBe("7");
|
|
288
|
+
expect(result.baseType).toBe("u8");
|
|
269
289
|
});
|
|
270
290
|
|
|
271
|
-
it("
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
291
|
+
it("returns false for 2D array element: matrix[0][1]", () => {
|
|
292
|
+
// matrix[0][1] is array access, not bit access
|
|
293
|
+
CodeGenState.typeRegistry.set("matrix", {
|
|
294
|
+
baseType: "u8",
|
|
295
|
+
bitWidth: 8,
|
|
296
|
+
isArray: true,
|
|
297
|
+
isConst: false,
|
|
298
|
+
arrayDimensions: [4, 4],
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const targetCtx = createTargetCtx("matrix", [
|
|
302
|
+
createSubscriptOp("0"),
|
|
303
|
+
createSubscriptOp("1"),
|
|
304
|
+
]);
|
|
277
305
|
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
306
|
+
const result = MemberChainAnalyzer.analyze(
|
|
307
|
+
targetCtx,
|
|
308
|
+
mockGenerateExpression,
|
|
309
|
+
);
|
|
282
310
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
311
|
+
// This is 2D array access, not bit access
|
|
312
|
+
expect(result.isBitAccess).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("detects bit access on 2D array element: matrix[0][1][3]", () => {
|
|
316
|
+
// matrix[0][1][3] where matrix is u8[4][4]
|
|
317
|
+
// The third subscript [3] is bit access on the u8 element
|
|
318
|
+
CodeGenState.typeRegistry.set("matrix", {
|
|
319
|
+
baseType: "u8",
|
|
320
|
+
bitWidth: 8,
|
|
321
|
+
isArray: true,
|
|
286
322
|
isConst: false,
|
|
287
|
-
|
|
323
|
+
arrayDimensions: [4, 4],
|
|
288
324
|
});
|
|
289
325
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
mockExpr,
|
|
307
|
-
{ getText: () => "]" },
|
|
308
|
-
],
|
|
309
|
-
};
|
|
326
|
+
const targetCtx = createTargetCtx("matrix", [
|
|
327
|
+
createSubscriptOp("0"),
|
|
328
|
+
createSubscriptOp("1"),
|
|
329
|
+
createSubscriptOp("3"),
|
|
330
|
+
]);
|
|
331
|
+
|
|
332
|
+
const result = MemberChainAnalyzer.analyze(
|
|
333
|
+
targetCtx,
|
|
334
|
+
mockGenerateExpression,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
expect(result.isBitAccess).toBe(true);
|
|
338
|
+
expect(result.baseTarget).toBe("matrix[0][1]");
|
|
339
|
+
expect(result.bitIndex).toBe("3");
|
|
340
|
+
expect(result.baseType).toBe("u8");
|
|
341
|
+
});
|
|
310
342
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
343
|
+
it("returns false for unknown base variable", () => {
|
|
344
|
+
// unknownVar.field[0] - unknownVar not in typeRegistry
|
|
345
|
+
const targetCtx = createTargetCtx("unknownVar", [
|
|
346
|
+
createMemberOp("field"),
|
|
347
|
+
createSubscriptOp("0"),
|
|
348
|
+
]);
|
|
314
349
|
|
|
315
|
-
const result =
|
|
350
|
+
const result = MemberChainAnalyzer.analyze(
|
|
351
|
+
targetCtx,
|
|
352
|
+
mockGenerateExpression,
|
|
353
|
+
);
|
|
316
354
|
|
|
317
355
|
expect(result.isBitAccess).toBe(false);
|
|
318
356
|
});
|
|
319
357
|
|
|
320
|
-
it("
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
typeRegistry.set("grid", {
|
|
328
|
-
baseType: "Pixel",
|
|
329
|
-
bitWidth: 0,
|
|
358
|
+
it("returns false for member access on non-struct", () => {
|
|
359
|
+
// x.field[0] where x is a primitive
|
|
360
|
+
CodeGenState.typeRegistry.set("x", {
|
|
361
|
+
baseType: "u32",
|
|
362
|
+
bitWidth: 32,
|
|
363
|
+
isArray: false,
|
|
330
364
|
isConst: false,
|
|
331
|
-
isArray: true,
|
|
332
365
|
});
|
|
333
366
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
IDENTIFIER: () => [
|
|
339
|
-
{ getText: () => "grid" },
|
|
340
|
-
{ getText: () => "flags" },
|
|
341
|
-
],
|
|
342
|
-
expression: () => [mockExpr0, mockExpr1],
|
|
343
|
-
children: [
|
|
344
|
-
{ getText: () => "grid" },
|
|
345
|
-
{ getText: () => "[" },
|
|
346
|
-
mockExpr0,
|
|
347
|
-
{ getText: () => "]" },
|
|
348
|
-
{ getText: () => "." },
|
|
349
|
-
{ getText: () => "flags" },
|
|
350
|
-
{ getText: () => "[" },
|
|
351
|
-
mockExpr1,
|
|
352
|
-
{ getText: () => "]" },
|
|
353
|
-
],
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
const targetCtx = {
|
|
357
|
-
memberAccess: () => memberAccessCtx,
|
|
358
|
-
} as never;
|
|
367
|
+
const targetCtx = createTargetCtx("x", [
|
|
368
|
+
createMemberOp("field"),
|
|
369
|
+
createSubscriptOp("0"),
|
|
370
|
+
]);
|
|
359
371
|
|
|
360
|
-
const result =
|
|
372
|
+
const result = MemberChainAnalyzer.analyze(
|
|
373
|
+
targetCtx,
|
|
374
|
+
mockGenerateExpression,
|
|
375
|
+
);
|
|
361
376
|
|
|
362
|
-
expect(result.isBitAccess).toBe(
|
|
363
|
-
expect(result.baseTarget).toBe("grid[0].flags");
|
|
364
|
-
expect(result.bitIndex).toBe("1");
|
|
377
|
+
expect(result.isBitAccess).toBe(false);
|
|
365
378
|
});
|
|
366
379
|
});
|
|
367
380
|
});
|