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
@@ -4,37 +4,16 @@
4
4
  * Analyzes an assignment context and determines which AssignmentKind it is.
5
5
  * The classification order matches the original generateAssignment() method's
6
6
  * if-else chain to ensure identical behavior.
7
+ *
8
+ * Migrated to use CodeGenState instead of constructor DI.
7
9
  */
8
10
  import AssignmentKind from "./AssignmentKind";
9
11
  import IAssignmentContext from "./IAssignmentContext";
10
- import ICodeGenSymbols from "../../../types/ICodeGenSymbols";
12
+ import CodeGenState from "../CodeGenState";
11
13
  import SubscriptClassifier from "../subscript/SubscriptClassifier";
12
14
  import TTypeInfo from "../types/TTypeInfo";
13
15
  import TypeCheckUtils from "../../../../utils/TypeCheckUtils";
14
16
 
15
- /**
16
- * Dependencies for classification.
17
- */
18
- interface IClassifierDeps {
19
- /** Symbol information (registers, bitmaps, structs, etc.) */
20
- readonly symbols: ICodeGenSymbols;
21
-
22
- /** Type registry: variable name -> type info */
23
- readonly typeRegistry: ReadonlyMap<string, TTypeInfo>;
24
-
25
- /** Current scope name, null if not in scope */
26
- readonly currentScope: string | null;
27
-
28
- /** Check if a type name is a known struct */
29
- isKnownStruct(typeName: string): boolean;
30
-
31
- /** Check if a name is a known scope */
32
- isKnownScope(name: string): boolean;
33
-
34
- /** Get member type info for a struct field */
35
- getMemberTypeInfo(structType: string, memberName: string): TTypeInfo | null;
36
- }
37
-
38
17
  /**
39
18
  * Classifies assignment statements by analyzing their structure.
40
19
  *
@@ -48,12 +27,10 @@ interface IClassifierDeps {
48
27
  * 7. Simple fallback
49
28
  */
50
29
  class AssignmentClassifier {
51
- constructor(private readonly deps: IClassifierDeps) {}
52
-
53
30
  /**
54
31
  * Check if typeInfo represents a simple string type (not a 2D+ string array).
55
32
  */
56
- private isSimpleStringType(typeInfo: TTypeInfo | undefined): boolean {
33
+ private static isSimpleStringType(typeInfo: TTypeInfo | undefined): boolean {
57
34
  return (
58
35
  typeInfo?.isString === true &&
59
36
  typeInfo.stringCapacity !== undefined &&
@@ -64,7 +41,7 @@ class AssignmentClassifier {
64
41
  /**
65
42
  * Extract struct name and field name from a 2-identifier context.
66
43
  */
67
- private getStructFieldNames(
44
+ private static getStructFieldNames(
68
45
  ctx: IAssignmentContext,
69
46
  ): { structName: string; fieldName: string } | null {
70
47
  if (ctx.identifiers.length !== 2) {
@@ -76,39 +53,40 @@ class AssignmentClassifier {
76
53
  /**
77
54
  * Classify an assignment context into an AssignmentKind.
78
55
  */
79
- classify(ctx: IAssignmentContext): AssignmentKind {
56
+ static classify(ctx: IAssignmentContext): AssignmentKind {
80
57
  // === Priority 1: Bitmap field assignments ===
81
- const bitmapKind = this.classifyBitmapField(ctx);
58
+ const bitmapKind = AssignmentClassifier.classifyBitmapField(ctx);
82
59
  if (bitmapKind !== null) {
83
60
  return bitmapKind;
84
61
  }
85
62
 
86
63
  // === Priority 2: Member access with subscripts (arrays, register bits) ===
87
- const memberSubscriptKind = this.classifyMemberWithSubscript(ctx);
64
+ const memberSubscriptKind =
65
+ AssignmentClassifier.classifyMemberWithSubscript(ctx);
88
66
  if (memberSubscriptKind !== null) {
89
67
  return memberSubscriptKind;
90
68
  }
91
69
 
92
70
  // === Priority 3: Global/this prefix patterns ===
93
- const prefixKind = this.classifyPrefixPattern(ctx);
71
+ const prefixKind = AssignmentClassifier.classifyPrefixPattern(ctx);
94
72
  if (prefixKind !== null) {
95
73
  return prefixKind;
96
74
  }
97
75
 
98
76
  // === Priority 4: Simple array/bit access ===
99
- const arrayBitKind = this.classifyArrayOrBitAccess(ctx);
77
+ const arrayBitKind = AssignmentClassifier.classifyArrayOrBitAccess(ctx);
100
78
  if (arrayBitKind !== null) {
101
79
  return arrayBitKind;
102
80
  }
103
81
 
104
82
  // === Priority 5: Atomic/overflow compound assignments ===
105
- const specialKind = this.classifySpecialCompound(ctx);
83
+ const specialKind = AssignmentClassifier.classifySpecialCompound(ctx);
106
84
  if (specialKind !== null) {
107
85
  return specialKind;
108
86
  }
109
87
 
110
88
  // === Priority 6: String assignments ===
111
- const stringKind = this.classifyStringAssignment(ctx);
89
+ const stringKind = AssignmentClassifier.classifyStringAssignment(ctx);
112
90
  if (stringKind !== null) {
113
91
  return stringKind;
114
92
  }
@@ -127,7 +105,9 @@ class AssignmentClassifier {
127
105
  * Classify bitmap field assignments.
128
106
  * Patterns: var.field, struct.bitmapMember.field, REG.MEMBER.field, Scope.REG.MEMBER.field
129
107
  */
130
- private classifyBitmapField(ctx: IAssignmentContext): AssignmentKind | null {
108
+ private static classifyBitmapField(
109
+ ctx: IAssignmentContext,
110
+ ): AssignmentKind | null {
131
111
  // Must have member access without subscripts
132
112
  if (!ctx.hasMemberAccess || ctx.hasArrayAccess) {
133
113
  return null;
@@ -139,15 +119,19 @@ class AssignmentClassifier {
139
119
  }
140
120
 
141
121
  if (ids.length === 2) {
142
- return this.classifySimpleBitmapField(ids[0], ids[1]);
122
+ return AssignmentClassifier.classifySimpleBitmapField(ids[0], ids[1]);
143
123
  }
144
124
 
145
125
  if (ids.length === 3) {
146
- return this.classifyThreeIdBitmapField(ids[0], ids[1], ids[2]);
126
+ return AssignmentClassifier.classifyThreeIdBitmapField(
127
+ ids[0],
128
+ ids[1],
129
+ ids[2],
130
+ );
147
131
  }
148
132
 
149
133
  if (ids.length === 4) {
150
- return this.classifyScopedRegisterBitmapField(ids);
134
+ return AssignmentClassifier.classifyScopedRegisterBitmapField(ids);
151
135
  }
152
136
 
153
137
  return null;
@@ -156,16 +140,16 @@ class AssignmentClassifier {
156
140
  /**
157
141
  * Classify 2-id bitmap field: var.field
158
142
  */
159
- private classifySimpleBitmapField(
143
+ private static classifySimpleBitmapField(
160
144
  varName: string,
161
145
  fieldName: string,
162
146
  ): AssignmentKind | null {
163
- const typeInfo = this.deps.typeRegistry.get(varName);
147
+ const typeInfo = CodeGenState.typeRegistry.get(varName);
164
148
  if (!typeInfo?.isBitmap || !typeInfo.bitmapTypeName) {
165
149
  return null;
166
150
  }
167
151
 
168
- const width = this.lookupBitmapFieldWidth(
152
+ const width = AssignmentClassifier.lookupBitmapFieldWidth(
169
153
  typeInfo.bitmapTypeName,
170
154
  fieldName,
171
155
  );
@@ -181,19 +165,22 @@ class AssignmentClassifier {
181
165
  /**
182
166
  * Classify 3-id bitmap field: REG.MEMBER.field or struct.bitmapMember.field
183
167
  */
184
- private classifyThreeIdBitmapField(
168
+ private static classifyThreeIdBitmapField(
185
169
  firstName: string,
186
170
  secondName: string,
187
171
  fieldName: string,
188
172
  ): AssignmentKind | null {
189
173
  // Check if register member bitmap field: REG.MEMBER.field
190
- if (this.deps.symbols.knownRegisters.has(firstName)) {
191
- const bitmapType = this.lookupRegisterMemberBitmapType(
174
+ if (CodeGenState.symbols!.knownRegisters.has(firstName)) {
175
+ const bitmapType = AssignmentClassifier.lookupRegisterMemberBitmapType(
192
176
  firstName,
193
177
  secondName,
194
178
  );
195
179
  if (bitmapType) {
196
- const width = this.lookupBitmapFieldWidth(bitmapType, fieldName);
180
+ const width = AssignmentClassifier.lookupBitmapFieldWidth(
181
+ bitmapType,
182
+ fieldName,
183
+ );
197
184
  if (width !== null) {
198
185
  return AssignmentKind.REGISTER_MEMBER_BITMAP_FIELD;
199
186
  }
@@ -202,12 +189,15 @@ class AssignmentClassifier {
202
189
  }
203
190
 
204
191
  // Check if struct member bitmap field: struct.bitmapMember.field
205
- const structTypeInfo = this.deps.typeRegistry.get(firstName);
206
- if (!structTypeInfo || !this.deps.isKnownStruct(structTypeInfo.baseType)) {
192
+ const structTypeInfo = CodeGenState.typeRegistry.get(firstName);
193
+ if (
194
+ !structTypeInfo ||
195
+ !CodeGenState.isKnownStruct(structTypeInfo.baseType)
196
+ ) {
207
197
  return null;
208
198
  }
209
199
 
210
- const memberInfo = this.deps.getMemberTypeInfo(
200
+ const memberInfo = CodeGenState.getMemberTypeInfo(
211
201
  structTypeInfo.baseType,
212
202
  secondName,
213
203
  );
@@ -215,7 +205,10 @@ class AssignmentClassifier {
215
205
  return null;
216
206
  }
217
207
 
218
- const width = this.lookupBitmapFieldWidth(memberInfo.baseType, fieldName);
208
+ const width = AssignmentClassifier.lookupBitmapFieldWidth(
209
+ memberInfo.baseType,
210
+ fieldName,
211
+ );
219
212
  if (width !== null) {
220
213
  return AssignmentKind.STRUCT_MEMBER_BITMAP_FIELD;
221
214
  }
@@ -226,25 +219,31 @@ class AssignmentClassifier {
226
219
  /**
227
220
  * Classify 4-id scoped register bitmap field: Scope.REG.MEMBER.field
228
221
  */
229
- private classifyScopedRegisterBitmapField(
222
+ private static classifyScopedRegisterBitmapField(
230
223
  ids: readonly string[],
231
224
  ): AssignmentKind | null {
232
225
  const scopeName = ids[0];
233
- if (!this.deps.isKnownScope(scopeName)) {
226
+ if (!CodeGenState.isKnownScope(scopeName)) {
234
227
  return null;
235
228
  }
236
229
 
237
230
  const fullRegName = `${scopeName}_${ids[1]}`;
238
- if (!this.deps.symbols.knownRegisters.has(fullRegName)) {
231
+ if (!CodeGenState.symbols!.knownRegisters.has(fullRegName)) {
239
232
  return null;
240
233
  }
241
234
 
242
- const bitmapType = this.lookupRegisterMemberBitmapType(fullRegName, ids[2]);
235
+ const bitmapType = AssignmentClassifier.lookupRegisterMemberBitmapType(
236
+ fullRegName,
237
+ ids[2],
238
+ );
243
239
  if (!bitmapType) {
244
240
  return null;
245
241
  }
246
242
 
247
- const width = this.lookupBitmapFieldWidth(bitmapType, ids[3]);
243
+ const width = AssignmentClassifier.lookupBitmapFieldWidth(
244
+ bitmapType,
245
+ ids[3],
246
+ );
248
247
  if (width !== null) {
249
248
  return AssignmentKind.SCOPED_REGISTER_MEMBER_BITMAP_FIELD;
250
249
  }
@@ -256,7 +255,7 @@ class AssignmentClassifier {
256
255
  * Classify member access with subscripts.
257
256
  * Patterns: arr[i][j], struct.arr[i], REG.MEMBER[bit], matrix[i][j][bit]
258
257
  */
259
- private classifyMemberWithSubscript(
258
+ private static classifyMemberWithSubscript(
260
259
  ctx: IAssignmentContext,
261
260
  ): AssignmentKind | null {
262
261
  // Need subscripts through memberAccess pattern
@@ -271,16 +270,25 @@ class AssignmentClassifier {
271
270
 
272
271
  const ids = ctx.identifiers;
273
272
  const firstId = ids[0];
274
- const typeInfo = this.deps.typeRegistry.get(firstId);
273
+ const typeInfo = CodeGenState.typeRegistry.get(firstId);
274
+
275
+ // Check for bit range through struct chain: devices[0].control[0, 4]
276
+ // Detected by last subscript having 2 expressions (start, width)
277
+ if (ctx.lastSubscriptExprCount === 2) {
278
+ return AssignmentKind.STRUCT_CHAIN_BIT_RANGE;
279
+ }
275
280
 
276
281
  // Multi-dimensional array element: arr[i][j] (1 identifier, multiple subscripts)
277
282
  if (ids.length === 1) {
278
- return this.classifyMultiDimArrayAccess(typeInfo, ctx.subscripts.length);
283
+ return AssignmentClassifier.classifyMultiDimArrayAccess(
284
+ typeInfo,
285
+ ctx.subscripts.length,
286
+ );
279
287
  }
280
288
 
281
289
  // 2+ identifiers with subscripts: register bit or bitmap array
282
290
  if (ids.length >= 2) {
283
- const registerKind = this.classifyRegisterBitAccess(
291
+ const registerKind = AssignmentClassifier.classifyRegisterBitAccess(
284
292
  ids,
285
293
  ctx.subscripts.length,
286
294
  );
@@ -288,7 +296,7 @@ class AssignmentClassifier {
288
296
  return registerKind;
289
297
  }
290
298
 
291
- return this.classifyBitmapArrayField(
299
+ return AssignmentClassifier.classifyBitmapArrayField(
292
300
  ids[1],
293
301
  typeInfo,
294
302
  ctx.subscripts.length,
@@ -301,7 +309,7 @@ class AssignmentClassifier {
301
309
  /**
302
310
  * Classify multi-dimensional array access: arr[i][j] or arr[i][j][bit]
303
311
  */
304
- private classifyMultiDimArrayAccess(
312
+ private static classifyMultiDimArrayAccess(
305
313
  typeInfo: TTypeInfo | undefined,
306
314
  subscriptCount: number,
307
315
  ): AssignmentKind | null {
@@ -325,16 +333,16 @@ class AssignmentClassifier {
325
333
  /**
326
334
  * Classify register bit access: REG.MEMBER[bit] or Scope.REG.MEMBER[bit]
327
335
  */
328
- private classifyRegisterBitAccess(
336
+ private static classifyRegisterBitAccess(
329
337
  ids: readonly string[],
330
338
  subscriptCount: number,
331
339
  ): AssignmentKind | null {
332
340
  const firstId = ids[0];
333
341
 
334
342
  // Check for scoped register: Scope.REG.MEMBER[bit]
335
- if (this.deps.isKnownScope(firstId) && ids.length >= 3) {
343
+ if (CodeGenState.isKnownScope(firstId) && ids.length >= 3) {
336
344
  const scopedRegName = `${firstId}_${ids[1]}`;
337
- if (this.deps.symbols.knownRegisters.has(scopedRegName)) {
345
+ if (CodeGenState.symbols!.knownRegisters.has(scopedRegName)) {
338
346
  return subscriptCount === 2
339
347
  ? AssignmentKind.REGISTER_BIT_RANGE
340
348
  : AssignmentKind.REGISTER_BIT;
@@ -342,7 +350,7 @@ class AssignmentClassifier {
342
350
  }
343
351
 
344
352
  // Check for non-scoped register: REG.MEMBER[bit]
345
- if (this.deps.symbols.knownRegisters.has(firstId)) {
353
+ if (CodeGenState.symbols!.knownRegisters.has(firstId)) {
346
354
  return subscriptCount === 2
347
355
  ? AssignmentKind.REGISTER_BIT_RANGE
348
356
  : AssignmentKind.REGISTER_BIT;
@@ -354,7 +362,7 @@ class AssignmentClassifier {
354
362
  /**
355
363
  * Classify bitmap array element field: bitmapArr[i].field
356
364
  */
357
- private classifyBitmapArrayField(
365
+ private static classifyBitmapArrayField(
358
366
  secondId: string,
359
367
  typeInfo: TTypeInfo | undefined,
360
368
  subscriptCount: number,
@@ -367,7 +375,7 @@ class AssignmentClassifier {
367
375
  return null;
368
376
  }
369
377
 
370
- const width = this.lookupBitmapFieldWidth(
378
+ const width = AssignmentClassifier.lookupBitmapFieldWidth(
371
379
  typeInfo.bitmapTypeName,
372
380
  secondId,
373
381
  );
@@ -381,7 +389,7 @@ class AssignmentClassifier {
381
389
  /**
382
390
  * Classify global.* and this.* prefix patterns.
383
391
  */
384
- private classifyPrefixPattern(
392
+ private static classifyPrefixPattern(
385
393
  ctx: IAssignmentContext,
386
394
  ): AssignmentKind | null {
387
395
  if (!ctx.hasGlobal && !ctx.hasThis) {
@@ -389,11 +397,11 @@ class AssignmentClassifier {
389
397
  }
390
398
 
391
399
  if (ctx.hasGlobal && ctx.postfixOpsCount > 0) {
392
- return this.classifyGlobalPrefix(ctx);
400
+ return AssignmentClassifier.classifyGlobalPrefix(ctx);
393
401
  }
394
402
 
395
403
  if (ctx.hasThis && ctx.postfixOpsCount > 0) {
396
- return this.classifyThisPrefix(ctx);
404
+ return AssignmentClassifier.classifyThisPrefix(ctx);
397
405
  }
398
406
 
399
407
  return null;
@@ -402,11 +410,11 @@ class AssignmentClassifier {
402
410
  /**
403
411
  * Classify global.* patterns: global.reg[bit], global.arr[i], global.member
404
412
  */
405
- private classifyGlobalPrefix(ctx: IAssignmentContext): AssignmentKind {
413
+ private static classifyGlobalPrefix(ctx: IAssignmentContext): AssignmentKind {
406
414
  const firstId = ctx.identifiers[0];
407
415
 
408
416
  if (ctx.hasArrayAccess) {
409
- if (this.deps.symbols.knownRegisters.has(firstId)) {
417
+ if (CodeGenState.symbols!.knownRegisters.has(firstId)) {
410
418
  return AssignmentKind.GLOBAL_REGISTER_BIT;
411
419
  }
412
420
  return AssignmentKind.GLOBAL_ARRAY;
@@ -418,24 +426,27 @@ class AssignmentClassifier {
418
426
  /**
419
427
  * Classify this.* patterns: this.reg[bit], this.member, this.REG.MEMBER.field
420
428
  */
421
- private classifyThisPrefix(ctx: IAssignmentContext): AssignmentKind {
422
- if (!this.deps.currentScope) {
429
+ private static classifyThisPrefix(ctx: IAssignmentContext): AssignmentKind {
430
+ if (!CodeGenState.currentScope) {
423
431
  return AssignmentKind.THIS_MEMBER;
424
432
  }
425
433
 
426
434
  const firstId = ctx.identifiers[0];
427
- const scopedRegName = `${this.deps.currentScope}_${firstId}`;
435
+ const scopedRegName = `${CodeGenState.currentScope}_${firstId}`;
428
436
 
429
437
  if (ctx.hasArrayAccess) {
430
- return this.classifyThisWithArrayAccess(ctx, scopedRegName);
438
+ return AssignmentClassifier.classifyThisWithArrayAccess(
439
+ ctx,
440
+ scopedRegName,
441
+ );
431
442
  }
432
443
 
433
444
  // this.REG.MEMBER.field (scoped register bitmap field)
434
445
  if (
435
446
  ctx.identifiers.length === 3 &&
436
- this.deps.symbols.knownRegisters.has(scopedRegName)
447
+ CodeGenState.symbols!.knownRegisters.has(scopedRegName)
437
448
  ) {
438
- const bitmapType = this.lookupRegisterMemberBitmapType(
449
+ const bitmapType = AssignmentClassifier.lookupRegisterMemberBitmapType(
439
450
  scopedRegName,
440
451
  ctx.identifiers[1],
441
452
  );
@@ -450,11 +461,11 @@ class AssignmentClassifier {
450
461
  /**
451
462
  * Classify this.reg[bit] / this.arr[i] patterns with array access.
452
463
  */
453
- private classifyThisWithArrayAccess(
464
+ private static classifyThisWithArrayAccess(
454
465
  ctx: IAssignmentContext,
455
466
  scopedRegName: string,
456
467
  ): AssignmentKind {
457
- if (this.deps.symbols.knownRegisters.has(scopedRegName)) {
468
+ if (CodeGenState.symbols!.knownRegisters.has(scopedRegName)) {
458
469
  const hasBitRange = ctx.postfixOps.some((op) => op.COMMA() !== null);
459
470
  return hasBitRange
460
471
  ? AssignmentKind.SCOPED_REGISTER_BIT_RANGE
@@ -470,7 +481,7 @@ class AssignmentClassifier {
470
481
  * Issue #579: Uses shared SubscriptClassifier to ensure consistent behavior
471
482
  * with the expression path in CodeGenerator._generatePostfixExpr.
472
483
  */
473
- private classifyArrayOrBitAccess(
484
+ private static classifyArrayOrBitAccess(
474
485
  ctx: IAssignmentContext,
475
486
  ): AssignmentKind | null {
476
487
  // Must have arrayAccess without memberAccess or prefix
@@ -483,17 +494,34 @@ class AssignmentClassifier {
483
494
  }
484
495
 
485
496
  const name = ctx.identifiers[0];
486
- const typeInfo = this.deps.typeRegistry.get(name) ?? null;
497
+ const typeInfo = CodeGenState.typeRegistry.get(name) ?? null;
487
498
 
488
499
  // Use shared classifier for array vs bit access decision
500
+ // Use lastSubscriptExprCount to distinguish [0][0] (two ops, each 1 expr)
501
+ // from [0, 5] (one op, 2 exprs)
489
502
  const subscriptKind = SubscriptClassifier.classify({
490
503
  typeInfo,
491
- subscriptCount: ctx.subscripts.length,
504
+ subscriptCount: ctx.lastSubscriptExprCount,
492
505
  isRegisterAccess: false,
493
506
  });
494
507
 
495
508
  switch (subscriptKind) {
496
509
  case "array_element":
510
+ // Multi-dimensional array: matrix[i][j] has multiple subscript operations
511
+ // but each with 1 expression (vs slice [0, 5] with 2 expressions in 1 op)
512
+ if (ctx.subscripts.length > 1) {
513
+ // Check if last subscript is bit access on an integer array element
514
+ // e.g., matrix[i][j][bit] where matrix is 2D integer array
515
+ const numDims = typeInfo?.arrayDimensions?.length ?? 0;
516
+ if (
517
+ ctx.subscripts.length === numDims + 1 &&
518
+ typeInfo &&
519
+ TypeCheckUtils.isInteger(typeInfo.baseType)
520
+ ) {
521
+ return AssignmentKind.ARRAY_ELEMENT_BIT;
522
+ }
523
+ return AssignmentKind.MULTI_DIM_ARRAY_ELEMENT;
524
+ }
497
525
  // String array element (special case for 2D string arrays)
498
526
  if (
499
527
  typeInfo?.isString &&
@@ -519,7 +547,7 @@ class AssignmentClassifier {
519
547
  * Classify atomic and overflow-clamped compound assignments.
520
548
  * Handles simple identifiers, this.member, and global.member patterns.
521
549
  */
522
- private classifySpecialCompound(
550
+ private static classifySpecialCompound(
523
551
  ctx: IAssignmentContext,
524
552
  ): AssignmentKind | null {
525
553
  if (!ctx.isCompound) {
@@ -530,16 +558,16 @@ class AssignmentClassifier {
530
558
  let typeInfo;
531
559
  if (ctx.isSimpleIdentifier) {
532
560
  const id = ctx.identifiers[0];
533
- typeInfo = this.deps.typeRegistry.get(id);
534
- } else if (ctx.isSimpleThisAccess && this.deps.currentScope) {
561
+ typeInfo = CodeGenState.typeRegistry.get(id);
562
+ } else if (ctx.isSimpleThisAccess && CodeGenState.currentScope) {
535
563
  // this.member pattern: lookup using scoped name
536
564
  const memberName = ctx.identifiers[0];
537
- const scopedName = `${this.deps.currentScope}_${memberName}`;
538
- typeInfo = this.deps.typeRegistry.get(scopedName);
565
+ const scopedName = `${CodeGenState.currentScope}_${memberName}`;
566
+ typeInfo = CodeGenState.typeRegistry.get(scopedName);
539
567
  } else if (ctx.isSimpleGlobalAccess) {
540
568
  // global.member pattern: lookup using direct name
541
569
  const memberName = ctx.identifiers[0];
542
- typeInfo = this.deps.typeRegistry.get(memberName);
570
+ typeInfo = CodeGenState.typeRegistry.get(memberName);
543
571
  } else {
544
572
  return null;
545
573
  }
@@ -571,13 +599,13 @@ class AssignmentClassifier {
571
599
  /**
572
600
  * Check if a simple identifier is a string variable.
573
601
  */
574
- private _classifySimpleStringVar(
602
+ private static _classifySimpleStringVar(
575
603
  ctx: IAssignmentContext,
576
604
  ): AssignmentKind | null {
577
605
  if (!ctx.isSimpleIdentifier) return null;
578
606
  const id = ctx.identifiers[0];
579
- const typeInfo = this.deps.typeRegistry.get(id);
580
- return this.isSimpleStringType(typeInfo)
607
+ const typeInfo = CodeGenState.typeRegistry.get(id);
608
+ return AssignmentClassifier.isSimpleStringType(typeInfo)
581
609
  ? AssignmentKind.STRING_SIMPLE
582
610
  : null;
583
611
  }
@@ -585,14 +613,14 @@ class AssignmentClassifier {
585
613
  /**
586
614
  * Check if this.member is a string.
587
615
  */
588
- private _classifyThisMemberString(
616
+ private static _classifyThisMemberString(
589
617
  ctx: IAssignmentContext,
590
618
  ): AssignmentKind | null {
591
- if (!ctx.isSimpleThisAccess || !this.deps.currentScope) return null;
619
+ if (!ctx.isSimpleThisAccess || !CodeGenState.currentScope) return null;
592
620
  const memberName = ctx.identifiers[0];
593
- const scopedName = `${this.deps.currentScope}_${memberName}`;
594
- const typeInfo = this.deps.typeRegistry.get(scopedName);
595
- return this.isSimpleStringType(typeInfo)
621
+ const scopedName = `${CodeGenState.currentScope}_${memberName}`;
622
+ const typeInfo = CodeGenState.typeRegistry.get(scopedName);
623
+ return AssignmentClassifier.isSimpleStringType(typeInfo)
596
624
  ? AssignmentKind.STRING_THIS_MEMBER
597
625
  : null;
598
626
  }
@@ -600,37 +628,67 @@ class AssignmentClassifier {
600
628
  /**
601
629
  * Check if global.member is a string.
602
630
  */
603
- private _classifyGlobalString(
631
+ private static _classifyGlobalString(
604
632
  ctx: IAssignmentContext,
605
633
  ): AssignmentKind | null {
606
634
  if (!ctx.isSimpleGlobalAccess) return null;
607
635
  const id = ctx.identifiers[0];
608
- const typeInfo = this.deps.typeRegistry.get(id);
609
- return this.isSimpleStringType(typeInfo)
636
+ const typeInfo = CodeGenState.typeRegistry.get(id);
637
+ return AssignmentClassifier.isSimpleStringType(typeInfo)
610
638
  ? AssignmentKind.STRING_GLOBAL
611
639
  : null;
612
640
  }
613
641
 
642
+ /**
643
+ * Resolve struct type from variable name.
644
+ * Returns the base struct type if valid, null if not a known struct.
645
+ */
646
+ private static _resolveStructType(structName: string): string | null {
647
+ const structTypeInfo = CodeGenState.typeRegistry.get(structName);
648
+ if (
649
+ !structTypeInfo ||
650
+ !CodeGenState.isKnownStruct(structTypeInfo.baseType)
651
+ ) {
652
+ return null;
653
+ }
654
+ return structTypeInfo.baseType;
655
+ }
656
+
657
+ /**
658
+ * Resolve struct field type from struct variable name and field name.
659
+ * Returns null if struct type can't be resolved or field doesn't exist.
660
+ */
661
+ private static _resolveStructFieldType(structFieldNames: {
662
+ structName: string;
663
+ fieldName: string;
664
+ }): { structType: string; fieldType: string | undefined } | null {
665
+ const structType = AssignmentClassifier._resolveStructType(
666
+ structFieldNames.structName,
667
+ );
668
+ if (!structType) {
669
+ return null;
670
+ }
671
+ const structFields = CodeGenState.symbols!.structFields.get(structType);
672
+ const fieldType = structFields?.get(structFieldNames.fieldName);
673
+ return { structType, fieldType };
674
+ }
675
+
614
676
  /**
615
677
  * Check if struct.field is a string field.
616
678
  */
617
- private _classifyStructFieldString(
679
+ private static _classifyStructFieldString(
618
680
  ctx: IAssignmentContext,
619
681
  structFieldNames: { structName: string; fieldName: string } | null,
620
682
  ): AssignmentKind | null {
621
683
  if (!ctx.hasMemberAccess || ctx.hasArrayAccess || !structFieldNames) {
622
684
  return null;
623
685
  }
624
- const { structName, fieldName } = structFieldNames;
625
- const structTypeInfo = this.deps.typeRegistry.get(structName);
626
- if (!structTypeInfo || !this.deps.isKnownStruct(structTypeInfo.baseType)) {
686
+ const resolved =
687
+ AssignmentClassifier._resolveStructFieldType(structFieldNames);
688
+ if (!resolved) {
627
689
  return null;
628
690
  }
629
- const structFields = this.deps.symbols.structFields.get(
630
- structTypeInfo.baseType,
631
- );
632
- const fieldType = structFields?.get(fieldName);
633
- return fieldType && TypeCheckUtils.isString(fieldType)
691
+ return resolved.fieldType && TypeCheckUtils.isString(resolved.fieldType)
634
692
  ? AssignmentKind.STRING_STRUCT_FIELD
635
693
  : null;
636
694
  }
@@ -638,7 +696,7 @@ class AssignmentClassifier {
638
696
  /**
639
697
  * Check if struct.arr[i] is a string array element.
640
698
  */
641
- private _classifyStructArrayElementString(
699
+ private static _classifyStructArrayElementString(
642
700
  ctx: IAssignmentContext,
643
701
  structFieldNames: { structName: string; fieldName: string } | null,
644
702
  ): AssignmentKind | null {
@@ -650,19 +708,19 @@ class AssignmentClassifier {
650
708
  ) {
651
709
  return null;
652
710
  }
653
- const { structName, fieldName } = structFieldNames;
654
- const structTypeInfo = this.deps.typeRegistry.get(structName);
655
- if (!structTypeInfo || !this.deps.isKnownStruct(structTypeInfo.baseType)) {
711
+ const resolved =
712
+ AssignmentClassifier._resolveStructFieldType(structFieldNames);
713
+ if (!resolved) {
656
714
  return null;
657
715
  }
658
716
 
659
- const structType = structTypeInfo.baseType;
660
- const structFields = this.deps.symbols.structFields.get(structType);
661
- const fieldType = structFields?.get(fieldName);
662
- const fieldArrays = this.deps.symbols.structFieldArrays.get(structType);
663
- const dimensions = this.deps.symbols.structFieldDimensions
664
- .get(structType)
665
- ?.get(fieldName);
717
+ const { structType, fieldType } = resolved;
718
+ const { fieldName } = structFieldNames;
719
+ const fieldArrays = CodeGenState.symbols!.structFieldArrays.get(structType);
720
+ const dimensions =
721
+ CodeGenState.symbols!.structFieldDimensions.get(structType)?.get(
722
+ fieldName,
723
+ );
666
724
 
667
725
  const isStringArrayField =
668
726
  fieldType &&
@@ -679,30 +737,34 @@ class AssignmentClassifier {
679
737
  /**
680
738
  * Classify string assignments.
681
739
  */
682
- private classifyStringAssignment(
740
+ private static classifyStringAssignment(
683
741
  ctx: IAssignmentContext,
684
742
  ): AssignmentKind | null {
685
743
  // Simple string variable
686
- const simpleVar = this._classifySimpleStringVar(ctx);
744
+ const simpleVar = AssignmentClassifier._classifySimpleStringVar(ctx);
687
745
  if (simpleVar) return simpleVar;
688
746
 
689
747
  // this.member string
690
- const thisMember = this._classifyThisMemberString(ctx);
748
+ const thisMember = AssignmentClassifier._classifyThisMemberString(ctx);
691
749
  if (thisMember) return thisMember;
692
750
 
693
751
  // global.member string
694
- const globalMember = this._classifyGlobalString(ctx);
752
+ const globalMember = AssignmentClassifier._classifyGlobalString(ctx);
695
753
  if (globalMember) return globalMember;
696
754
 
697
755
  // struct.field or struct.arr[i] string
698
- const structFieldNames = this.getStructFieldNames(ctx);
699
- const structField = this._classifyStructFieldString(ctx, structFieldNames);
700
- if (structField) return structField;
701
-
702
- const structArrayElement = this._classifyStructArrayElementString(
756
+ const structFieldNames = AssignmentClassifier.getStructFieldNames(ctx);
757
+ const structField = AssignmentClassifier._classifyStructFieldString(
703
758
  ctx,
704
759
  structFieldNames,
705
760
  );
761
+ if (structField) return structField;
762
+
763
+ const structArrayElement =
764
+ AssignmentClassifier._classifyStructArrayElementString(
765
+ ctx,
766
+ structFieldNames,
767
+ );
706
768
  if (structArrayElement) return structArrayElement;
707
769
 
708
770
  return null;
@@ -712,11 +774,11 @@ class AssignmentClassifier {
712
774
  * Look up a bitmap field's width by bitmap type name and field name.
713
775
  * Returns the field width if found, or null if the bitmap/field doesn't exist.
714
776
  */
715
- private lookupBitmapFieldWidth(
777
+ private static lookupBitmapFieldWidth(
716
778
  bitmapTypeName: string,
717
779
  fieldName: string,
718
780
  ): number | null {
719
- const fields = this.deps.symbols.bitmapFields.get(bitmapTypeName);
781
+ const fields = CodeGenState.symbols!.bitmapFields.get(bitmapTypeName);
720
782
  if (fields?.has(fieldName)) {
721
783
  return fields.get(fieldName)!.width;
722
784
  }
@@ -727,12 +789,12 @@ class AssignmentClassifier {
727
789
  * Look up the bitmap type for a register member (e.g., "REG_MEMBER" -> "BitmapType").
728
790
  * Returns the bitmap type name if found, or null.
729
791
  */
730
- private lookupRegisterMemberBitmapType(
792
+ private static lookupRegisterMemberBitmapType(
731
793
  registerName: string,
732
794
  memberName: string,
733
795
  ): string | null {
734
796
  const key = `${registerName}_${memberName}`;
735
- return this.deps.symbols.registerMemberTypes.get(key) ?? null;
797
+ return CodeGenState.symbols!.registerMemberTypes.get(key) ?? null;
736
798
  }
737
799
  }
738
800