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.
Files changed (104) hide show
  1. package/README.md +86 -63
  2. package/grammar/CNext.g4 +3 -17
  3. package/package.json +1 -1
  4. package/src/cli/serve/ServeCommand.ts +57 -45
  5. package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
  6. package/src/transpiler/Transpiler.ts +603 -613
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
  8. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
  9. package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
  10. package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
  11. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
  12. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
  13. package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
  14. package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
  15. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
  16. package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
  17. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  20. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
  22. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  23. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  24. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  25. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
  26. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  27. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  28. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  29. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  30. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
  31. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
  32. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
  33. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  34. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
  35. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
  36. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
  37. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
  38. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
  39. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
  41. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  42. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
  43. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  44. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  45. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
  46. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
  47. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
  48. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
  49. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  50. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  51. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  52. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
  53. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
  54. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  55. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
  56. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  57. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
  58. package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
  59. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
  60. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  61. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  62. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  63. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
  64. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  65. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
  66. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  67. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
  68. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  69. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  70. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  71. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  72. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  73. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  74. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
  75. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  76. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
  77. package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
  78. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  79. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  80. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  81. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  82. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  83. package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
  84. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
  85. package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
  86. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
  87. package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
  88. package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
  89. package/src/transpiler/types/IFileResult.ts +0 -4
  90. package/src/transpiler/types/IPipelineFile.ts +27 -0
  91. package/src/transpiler/types/IPipelineInput.ts +23 -0
  92. package/src/transpiler/types/TranspilerState.ts +1 -1
  93. package/src/utils/FormatUtils.ts +28 -2
  94. package/src/utils/MapUtils.ts +25 -0
  95. package/src/utils/PostfixAnalysisUtils.ts +48 -0
  96. package/src/utils/__tests__/FormatUtils.test.ts +42 -0
  97. package/src/utils/__tests__/MapUtils.test.ts +85 -0
  98. package/src/utils/constants/OperatorMappings.ts +19 -0
  99. package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
  100. package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
  101. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  102. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
  103. package/src/transpiler/types/ITranspileContext.ts +0 -49
  104. 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, vi } from "vitest";
9
+ import { describe, it, expect, beforeEach } from "vitest";
8
10
  import MemberChainAnalyzer from "../MemberChainAnalyzer.js";
9
- import type TTypeInfo from "../../types/TTypeInfo.js";
11
+ import CodeGenState from "../../CodeGenState.js";
10
12
  import type * as Parser from "../../../../logic/parser/grammar/CNextParser.js";
11
13
 
12
- /** Test-local copy of IMemberChainAnalyzerDeps interface */
13
- interface IMemberChainAnalyzerDeps {
14
- typeRegistry: ReadonlyMap<string, TTypeInfo>;
15
- structFields: ReadonlyMap<string, ReadonlyMap<string, string>>;
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
- describe("MemberChainAnalyzer", () => {
22
- let typeRegistry: Map<string, TTypeInfo>;
23
- let structFields: Map<string, Map<string, string>>;
24
- let structFieldArrays: Map<string, Set<string>>;
25
- let deps: IMemberChainAnalyzerDeps;
26
- let analyzer: MemberChainAnalyzer;
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
- typeRegistry = new Map();
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
- describe("analyze", () => {
45
- it("returns isBitAccess false when no memberAccess context", () => {
46
- // Create mock context without memberAccess
47
- const targetCtx = {
48
- memberAccess: () => null,
49
- } as never;
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
- const result = analyzer.analyze(targetCtx);
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 no expressions", () => {
57
- const memberAccessCtx = {
58
- IDENTIFIER: () => [{ getText: () => "point" }],
59
- expression: () => [],
60
- children: [{ getText: () => "point" }],
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
- memberAccess: () => memberAccessCtx,
65
- } as never;
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
- const result = analyzer.analyze(targetCtx);
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
- structFields.set("Point", pointFields);
77
- structFieldArrays.set("Point", new Set());
181
+ setupStructFields("Point", pointFields);
78
182
 
79
- // Variable 'point' is of type Point
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
- // Mock: point.flags[3]
88
- const mockExpr = { getText: () => "3" };
89
- const memberAccessCtx = {
90
- IDENTIFIER: () => [
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 = analyzer.analyze(targetCtx);
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 isBitAccess false for array subscript on array field", () => {
118
- // Setup: struct Data { u8 values[10]; }
119
- const dataFields = new Map<string, string>();
120
- dataFields.set("values", "u8");
121
- structFields.set("Data", dataFields);
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
- // Variable 'data' is of type Data
125
- typeRegistry.set("data", {
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
- // Mock: data.values[3]
133
- const mockExpr = { getText: () => "3" };
134
- const memberAccessCtx = {
135
- IDENTIFIER: () => [
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 = analyzer.analyze(targetCtx);
224
+ const result = MemberChainAnalyzer.analyze(
225
+ targetCtx,
226
+ mockGenerateExpression,
227
+ );
155
228
 
156
- // values is an array, so [3] is array subscript, not bit access
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 isBitAccess false for non-integer field types", () => {
161
- // Setup: struct Config { f32 value; }
162
- const configFields = new Map<string, string>();
163
- configFields.set("value", "f32");
164
- structFields.set("Config", configFields);
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("config", {
168
- baseType: "Config",
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
- // Mock: config.value[3]
175
- const mockExpr = { getText: () => "3" };
176
- const memberAccessCtx = {
177
- IDENTIFIER: () => [
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 = analyzer.analyze(targetCtx);
251
+ const result = MemberChainAnalyzer.analyze(
252
+ targetCtx,
253
+ mockGenerateExpression,
254
+ );
197
255
 
198
- // f32 is not an integer type, so this would be handled differently
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("returns isBitAccess false when children is null", () => {
204
- const memberAccessCtx = {
205
- IDENTIFIER: () => [{ getText: () => "point" }],
206
- expression: () => [{ getText: () => "0" }],
207
- children: null,
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
- expect(result.isBitAccess).toBe(false);
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
- isArray: false,
271
+ arrayDimensions: [4],
237
272
  });
238
273
 
239
- // Mock: obj.child.flags[0]
240
- const mockExpr = { getText: () => "0" };
241
- const memberAccessCtx = {
242
- IDENTIFIER: () => [
243
- { getText: () => "obj" },
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 targetCtx = {
261
- memberAccess: () => memberAccessCtx,
262
- } as never;
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("obj.child.flags");
268
- expect(result.bitIndex).toBe("0");
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("should return false for nested struct non-integer field subscript", () => {
272
- // Setup: struct Inner { f32 value; }, struct Outer { Inner child; }
273
- const innerFields = new Map<string, string>();
274
- innerFields.set("value", "f32");
275
- structFields.set("Inner", innerFields);
276
- structFieldArrays.set("Inner", new Set());
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 outerFields = new Map<string, string>();
279
- outerFields.set("child", "Inner");
280
- structFields.set("Outer", outerFields);
281
- structFieldArrays.set("Outer", new Set());
306
+ const result = MemberChainAnalyzer.analyze(
307
+ targetCtx,
308
+ mockGenerateExpression,
309
+ );
282
310
 
283
- typeRegistry.set("obj", {
284
- baseType: "Outer",
285
- bitWidth: 0,
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
- isArray: false,
323
+ arrayDimensions: [4, 4],
288
324
  });
289
325
 
290
- // Mock: obj.child.value[0]
291
- const mockExpr = { getText: () => "0" };
292
- const memberAccessCtx = {
293
- IDENTIFIER: () => [
294
- { getText: () => "obj" },
295
- { getText: () => "child" },
296
- { getText: () => "value" },
297
- ],
298
- expression: () => [mockExpr],
299
- children: [
300
- { getText: () => "obj" },
301
- { getText: () => "." },
302
- { getText: () => "child" },
303
- { getText: () => "." },
304
- { getText: () => "value" },
305
- { getText: () => "[" },
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
- const targetCtx = {
312
- memberAccess: () => memberAccessCtx,
313
- } as never;
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 = analyzer.analyze(targetCtx);
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("should detect bit access through array-of-structs subscript", () => {
321
- // Setup: struct Pixel { u8 flags; }, variable grid:Pixel isArray=true
322
- const pixelFields = new Map<string, string>();
323
- pixelFields.set("flags", "u8");
324
- structFields.set("Pixel", pixelFields);
325
- structFieldArrays.set("Pixel", new Set());
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
- // Mock: grid[0].flags[1]
335
- const mockExpr0 = { getText: () => "0" };
336
- const mockExpr1 = { getText: () => "1" };
337
- const memberAccessCtx = {
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 = analyzer.analyze(targetCtx);
372
+ const result = MemberChainAnalyzer.analyze(
373
+ targetCtx,
374
+ mockGenerateExpression,
375
+ );
361
376
 
362
- expect(result.isBitAccess).toBe(true);
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
  });