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
@@ -7,11 +7,12 @@
7
7
  * Used to detect bit access at the end of member chains, e.g.:
8
8
  * - grid[2][3].flags[0] - detects that [0] is bit access on flags
9
9
  * - point.x[3, 4] - detects bit range access on integer field
10
+ *
11
+ * Migrated to use CodeGenState instead of constructor DI.
10
12
  */
11
13
 
12
14
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
13
- import TTypeInfo from "../types/TTypeInfo.js";
14
- import memberAccessChain from "../memberAccessChain.js";
15
+ import CodeGenState from "../CodeGenState.js";
15
16
 
16
17
  /**
17
18
  * Result of analyzing a member chain for bit access.
@@ -28,19 +29,16 @@ interface IBitAccessAnalysisResult {
28
29
  }
29
30
 
30
31
  /**
31
- * Dependencies required for member chain analysis.
32
+ * Callback type for generating expression code.
32
33
  */
33
- interface IMemberChainAnalyzerDeps {
34
- /** Type registry for looking up variable types */
35
- typeRegistry: ReadonlyMap<string, TTypeInfo>;
36
- /** Struct field types by struct name */
37
- structFields: ReadonlyMap<string, ReadonlyMap<string, string>>;
38
- /** Struct field array flags by struct name */
39
- structFieldArrays: ReadonlyMap<string, ReadonlySet<string>>;
40
- /** Function to check if a type name is a known struct */
41
- isKnownStruct: (name: string) => boolean;
42
- /** Function to generate expression code */
43
- generateExpression: (ctx: Parser.ExpressionContext) => string;
34
+ type GenerateExpressionFn = (ctx: Parser.ExpressionContext) => string;
35
+
36
+ /** Mutable state for tracking types through a member chain. */
37
+ interface IChainState {
38
+ currentType: string;
39
+ currentStructType: string | undefined;
40
+ isCurrentArray: boolean;
41
+ arrayDimsRemaining: number;
44
42
  }
45
43
 
46
44
  /**
@@ -50,74 +48,237 @@ interface IMemberChainAnalyzerDeps {
50
48
  * detection callbacks to determine if the final subscript is bit access.
51
49
  */
52
50
  class MemberChainAnalyzer {
53
- private readonly deps: IMemberChainAnalyzerDeps;
54
-
55
- constructor(deps: IMemberChainAnalyzerDeps) {
56
- this.deps = deps;
57
- }
58
-
59
51
  /**
60
52
  * Analyze a member chain target to detect bit access at the end.
61
53
  *
62
54
  * For patterns like grid[2][3].flags[0], detects that [0] is bit access.
63
- * Delegates to buildMemberAccessChain which handles the tree-walking and
64
- * type tracking logic.
55
+ * Uses direct postfixTargetOp analysis with type tracking.
65
56
  *
66
57
  * @param targetCtx - The assignment target context to analyze
58
+ * @param generateExpression - Callback to generate expression code
67
59
  * @returns Analysis result with bit access information
68
60
  */
69
- analyze(targetCtx: Parser.AssignmentTargetContext): IBitAccessAnalysisResult {
70
- const memberAccessCtx = targetCtx.memberAccess();
71
- if (!memberAccessCtx) {
61
+ static analyze(
62
+ targetCtx: Parser.AssignmentTargetContext,
63
+ generateExpression: GenerateExpressionFn,
64
+ ): IBitAccessAnalysisResult {
65
+ const baseId = targetCtx.IDENTIFIER()?.getText();
66
+ const postfixOps = targetCtx.postfixTargetOp();
67
+
68
+ if (!baseId || postfixOps.length === 0) {
69
+ return { isBitAccess: false };
70
+ }
71
+
72
+ // Check if the last postfix op is a single-expression subscript (potential bit access)
73
+ const lastOp = postfixOps.at(-1)!;
74
+ const lastExprs = lastOp.expression();
75
+ if (lastExprs.length !== 1 || lastOp.IDENTIFIER()) {
76
+ // Last op is member access or multi-expression subscript, not bit access
77
+ return { isBitAccess: false };
78
+ }
79
+
80
+ // Count total subscript operations
81
+ const subscriptCount = postfixOps.filter(
82
+ (op) => !op.IDENTIFIER() && op.expression().length > 0,
83
+ ).length;
84
+
85
+ // Walk through the chain to find the type and array status before the last subscript
86
+ const targetInfo = MemberChainAnalyzer.resolveTargetTypeAndArrayStatus(
87
+ baseId,
88
+ postfixOps.slice(0, -1),
89
+ subscriptCount - 1, // subscripts before the last one
90
+ );
91
+ if (!targetInfo) {
72
92
  return { isBitAccess: false };
73
93
  }
74
94
 
75
- const parts = memberAccessCtx.IDENTIFIER().map((id) => id.getText());
76
- const expressions = memberAccessCtx.expression();
77
- const children = memberAccessCtx.children;
95
+ // If the target is still an array, the last subscript is array access, not bit access
96
+ if (targetInfo.isArray) {
97
+ return { isBitAccess: false };
98
+ }
78
99
 
79
- if (!children || parts.length < 1 || expressions.length === 0) {
100
+ // Check if the type is an integer (bit access only works on integers)
101
+ if (!MemberChainAnalyzer.isIntegerType(targetInfo.type)) {
80
102
  return { isBitAccess: false };
81
103
  }
82
104
 
83
- const firstPart = parts[0];
84
- const firstTypeInfo = this.deps.typeRegistry.get(firstPart);
85
-
86
- // Track bit access result via callback
87
- let bitAccessResult: IBitAccessAnalysisResult | null = null;
88
-
89
- memberAccessChain.buildMemberAccessChain({
90
- firstId: firstPart,
91
- identifiers: parts,
92
- expressions,
93
- children,
94
- separatorOptions: {
95
- isStructParam: false,
96
- isCrossScope: false,
97
- },
98
- generateExpression: this.deps.generateExpression,
99
- initialTypeInfo: firstTypeInfo
100
- ? { isArray: firstTypeInfo.isArray, baseType: firstTypeInfo.baseType }
105
+ // Build the base target expression (everything except the last subscript)
106
+ const baseTarget = MemberChainAnalyzer.buildBaseTarget(
107
+ baseId,
108
+ postfixOps.slice(0, -1),
109
+ generateExpression,
110
+ );
111
+ const bitIndex = generateExpression(lastExprs[0]);
112
+
113
+ return {
114
+ isBitAccess: true,
115
+ baseTarget,
116
+ bitIndex,
117
+ baseType: targetInfo.type,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Resolve the type and array status of the target by walking through postfix operations.
123
+ * Returns the type and whether it's still an array before the last subscript.
124
+ */
125
+ private static resolveTargetTypeAndArrayStatus(
126
+ baseId: string,
127
+ ops: Parser.PostfixTargetOpContext[],
128
+ _subscriptsSoFar: number,
129
+ ): { type: string; isArray: boolean } | undefined {
130
+ const baseTypeInfo = CodeGenState.typeRegistry.get(baseId);
131
+ if (!baseTypeInfo) {
132
+ return undefined;
133
+ }
134
+
135
+ const state: IChainState = {
136
+ currentType: baseTypeInfo.baseType,
137
+ currentStructType: CodeGenState.isKnownStruct(baseTypeInfo.baseType)
138
+ ? baseTypeInfo.baseType
101
139
  : undefined,
102
- typeTracking: {
103
- getStructFields: (structType) => this.deps.structFields.get(structType),
104
- getStructArrayFields: (structType) =>
105
- this.deps.structFieldArrays.get(structType),
106
- isKnownStruct: this.deps.isKnownStruct,
107
- },
108
- onBitAccess: (baseTarget, bitIndex, memberType) => {
109
- bitAccessResult = {
110
- isBitAccess: true,
111
- baseTarget,
112
- bitIndex,
113
- baseType: memberType,
114
- };
115
- // Return non-null to signal early exit from buildMemberAccessChain
116
- return baseTarget;
117
- },
118
- });
119
-
120
- return bitAccessResult ?? { isBitAccess: false };
140
+ isCurrentArray: baseTypeInfo.isArray,
141
+ arrayDimsRemaining: baseTypeInfo.arrayDimensions?.length ?? 0,
142
+ };
143
+
144
+ for (let i = 0; i < ops.length; i++) {
145
+ const op = ops[i];
146
+ if (op.IDENTIFIER()) {
147
+ const result = MemberChainAnalyzer.processMemberOp(op, ops, i, state);
148
+ if (!result) {
149
+ return undefined;
150
+ }
151
+ } else {
152
+ MemberChainAnalyzer.processSubscriptOp(state);
153
+ }
154
+ }
155
+
156
+ return { type: state.currentType, isArray: state.isCurrentArray };
157
+ }
158
+
159
+ /**
160
+ * Process a member access operation (.fieldName) and update chain state.
161
+ * Returns false if the access is invalid.
162
+ */
163
+ private static processMemberOp(
164
+ op: Parser.PostfixTargetOpContext,
165
+ ops: Parser.PostfixTargetOpContext[],
166
+ opIndex: number,
167
+ state: IChainState,
168
+ ): boolean {
169
+ const fieldName = op.IDENTIFIER()!.getText();
170
+ if (!state.currentStructType) {
171
+ return false;
172
+ }
173
+
174
+ const structFields = CodeGenState.symbols?.structFields.get(
175
+ state.currentStructType,
176
+ );
177
+ if (!structFields) {
178
+ return false;
179
+ }
180
+
181
+ const fieldType = structFields.get(fieldName);
182
+ if (!fieldType) {
183
+ return false;
184
+ }
185
+
186
+ state.currentType = fieldType;
187
+
188
+ // Check if this field is an array
189
+ const arrayFields = CodeGenState.symbols?.structFieldArrays.get(
190
+ state.currentStructType,
191
+ );
192
+ state.isCurrentArray = arrayFields?.has(fieldName) ?? false;
193
+
194
+ // If the field type is a struct, update currentStructType
195
+ state.currentStructType = CodeGenState.isKnownStruct(state.currentType)
196
+ ? state.currentType
197
+ : undefined;
198
+
199
+ // Calculate array dimensions remaining based on remaining subscripts
200
+ state.arrayDimsRemaining = state.isCurrentArray
201
+ ? MemberChainAnalyzer.countRemainingSubscripts(ops, opIndex) + 1
202
+ : 0;
203
+
204
+ return true;
205
+ }
206
+
207
+ /**
208
+ * Count remaining subscript operations after the given index.
209
+ */
210
+ private static countRemainingSubscripts(
211
+ ops: Parser.PostfixTargetOpContext[],
212
+ afterIndex: number,
213
+ ): number {
214
+ return ops
215
+ .slice(afterIndex + 1)
216
+ .filter((o) => !o.IDENTIFIER() && o.expression().length > 0).length;
217
+ }
218
+
219
+ /**
220
+ * Process a subscript operation ([expr]) and update chain state.
221
+ */
222
+ private static processSubscriptOp(state: IChainState): void {
223
+ if (!state.isCurrentArray || state.arrayDimsRemaining <= 0) {
224
+ return;
225
+ }
226
+
227
+ state.arrayDimsRemaining--;
228
+ if (state.arrayDimsRemaining === 0) {
229
+ state.isCurrentArray = false;
230
+ state.currentStructType = CodeGenState.isKnownStruct(state.currentType)
231
+ ? state.currentType
232
+ : undefined;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Check if a type is an integer type.
238
+ */
239
+ private static isIntegerType(typeName: string): boolean {
240
+ const intTypes = new Set([
241
+ "u8",
242
+ "u16",
243
+ "u32",
244
+ "u64",
245
+ "i8",
246
+ "i16",
247
+ "i32",
248
+ "i64",
249
+ ]);
250
+ return intTypes.has(typeName);
251
+ }
252
+
253
+ /**
254
+ * Build the target expression string from base identifier and postfix operations.
255
+ */
256
+ private static buildBaseTarget(
257
+ baseId: string,
258
+ ops: Parser.PostfixTargetOpContext[],
259
+ generateExpression: GenerateExpressionFn,
260
+ ): string {
261
+ let result = baseId;
262
+
263
+ for (const op of ops) {
264
+ if (op.IDENTIFIER()) {
265
+ result += "." + op.IDENTIFIER()!.getText();
266
+ } else {
267
+ const exprs = op.expression();
268
+ if (exprs.length === 1) {
269
+ result += "[" + generateExpression(exprs[0]) + "]";
270
+ } else if (exprs.length === 2) {
271
+ result +=
272
+ "[" +
273
+ generateExpression(exprs[0]) +
274
+ ", " +
275
+ generateExpression(exprs[1]) +
276
+ "]";
277
+ }
278
+ }
279
+ }
280
+
281
+ return result;
121
282
  }
122
283
  }
123
284
 
@@ -4,43 +4,34 @@
4
4
  * Issue #644: Extracted from CodeGenerator to reduce code duplication.
5
5
  * Used for strlen caching optimization - when a string's .length is accessed
6
6
  * multiple times, we cache the strlen result in a temp variable.
7
+ *
8
+ * Migrated to use CodeGenState instead of constructor DI.
7
9
  */
8
10
 
9
11
  import * as Parser from "../../../logic/parser/grammar/CNextParser";
10
- import TTypeInfo from "../types/TTypeInfo";
11
-
12
- /**
13
- * Type registry lookup function signature.
14
- */
15
- type TypeRegistryLookup = (name: string) => TTypeInfo | undefined;
12
+ import CodeGenState from "../CodeGenState";
16
13
 
17
14
  /**
18
15
  * Counts .length accesses on string variables in an expression tree.
19
16
  * This enables strlen caching optimization.
20
17
  */
21
18
  class StringLengthCounter {
22
- private readonly typeRegistry: TypeRegistryLookup;
23
-
24
- constructor(typeRegistry: TypeRegistryLookup) {
25
- this.typeRegistry = typeRegistry;
26
- }
27
-
28
19
  /**
29
20
  * Count .length accesses in an expression.
30
21
  */
31
- countExpression(ctx: Parser.ExpressionContext): Map<string, number> {
22
+ static countExpression(ctx: Parser.ExpressionContext): Map<string, number> {
32
23
  const counts = new Map<string, number>();
33
- this.walkExpression(ctx, counts);
24
+ StringLengthCounter.walkExpression(ctx, counts);
34
25
  return counts;
35
26
  }
36
27
 
37
28
  /**
38
29
  * Count .length accesses in a block.
39
30
  */
40
- countBlock(ctx: Parser.BlockContext): Map<string, number> {
31
+ static countBlock(ctx: Parser.BlockContext): Map<string, number> {
41
32
  const counts = new Map<string, number>();
42
33
  for (const stmt of ctx.statement()) {
43
- this.walkStatement(stmt, counts);
34
+ StringLengthCounter.walkStatement(stmt, counts);
44
35
  }
45
36
  return counts;
46
37
  }
@@ -48,9 +39,12 @@ class StringLengthCounter {
48
39
  /**
49
40
  * Count .length accesses in a block, adding to existing counts.
50
41
  */
51
- countBlockInto(ctx: Parser.BlockContext, counts: Map<string, number>): void {
42
+ static countBlockInto(
43
+ ctx: Parser.BlockContext,
44
+ counts: Map<string, number>,
45
+ ): void {
52
46
  for (const stmt of ctx.statement()) {
53
- this.walkStatement(stmt, counts);
47
+ StringLengthCounter.walkStatement(stmt, counts);
54
48
  }
55
49
  }
56
50
 
@@ -58,134 +52,134 @@ class StringLengthCounter {
58
52
  * Walk an expression tree, counting .length accesses.
59
53
  * Uses generic traversal - only postfix expressions need special handling.
60
54
  */
61
- private walkExpression(
55
+ private static walkExpression(
62
56
  ctx: Parser.ExpressionContext,
63
57
  counts: Map<string, number>,
64
58
  ): void {
65
59
  const ternary = ctx.ternaryExpression();
66
60
  if (ternary) {
67
- this.walkTernary(ternary, counts);
61
+ StringLengthCounter.walkTernary(ternary, counts);
68
62
  }
69
63
  }
70
64
 
71
- private walkTernary(
65
+ private static walkTernary(
72
66
  ctx: Parser.TernaryExpressionContext,
73
67
  counts: Map<string, number>,
74
68
  ): void {
75
69
  for (const orExpr of ctx.orExpression()) {
76
- this.walkOrExpr(orExpr, counts);
70
+ StringLengthCounter.walkOrExpr(orExpr, counts);
77
71
  }
78
72
  }
79
73
 
80
- private walkOrExpr(
74
+ private static walkOrExpr(
81
75
  ctx: Parser.OrExpressionContext,
82
76
  counts: Map<string, number>,
83
77
  ): void {
84
78
  for (const andExpr of ctx.andExpression()) {
85
- this.walkAndExpr(andExpr, counts);
79
+ StringLengthCounter.walkAndExpr(andExpr, counts);
86
80
  }
87
81
  }
88
82
 
89
- private walkAndExpr(
83
+ private static walkAndExpr(
90
84
  ctx: Parser.AndExpressionContext,
91
85
  counts: Map<string, number>,
92
86
  ): void {
93
87
  for (const eqExpr of ctx.equalityExpression()) {
94
- this.walkEqualityExpr(eqExpr, counts);
88
+ StringLengthCounter.walkEqualityExpr(eqExpr, counts);
95
89
  }
96
90
  }
97
91
 
98
- private walkEqualityExpr(
92
+ private static walkEqualityExpr(
99
93
  ctx: Parser.EqualityExpressionContext,
100
94
  counts: Map<string, number>,
101
95
  ): void {
102
96
  for (const relExpr of ctx.relationalExpression()) {
103
- this.walkRelationalExpr(relExpr, counts);
97
+ StringLengthCounter.walkRelationalExpr(relExpr, counts);
104
98
  }
105
99
  }
106
100
 
107
- private walkRelationalExpr(
101
+ private static walkRelationalExpr(
108
102
  ctx: Parser.RelationalExpressionContext,
109
103
  counts: Map<string, number>,
110
104
  ): void {
111
105
  for (const borExpr of ctx.bitwiseOrExpression()) {
112
- this.walkBitwiseOrExpr(borExpr, counts);
106
+ StringLengthCounter.walkBitwiseOrExpr(borExpr, counts);
113
107
  }
114
108
  }
115
109
 
116
- private walkBitwiseOrExpr(
110
+ private static walkBitwiseOrExpr(
117
111
  ctx: Parser.BitwiseOrExpressionContext,
118
112
  counts: Map<string, number>,
119
113
  ): void {
120
114
  for (const bxorExpr of ctx.bitwiseXorExpression()) {
121
- this.walkBitwiseXorExpr(bxorExpr, counts);
115
+ StringLengthCounter.walkBitwiseXorExpr(bxorExpr, counts);
122
116
  }
123
117
  }
124
118
 
125
- private walkBitwiseXorExpr(
119
+ private static walkBitwiseXorExpr(
126
120
  ctx: Parser.BitwiseXorExpressionContext,
127
121
  counts: Map<string, number>,
128
122
  ): void {
129
123
  for (const bandExpr of ctx.bitwiseAndExpression()) {
130
- this.walkBitwiseAndExpr(bandExpr, counts);
124
+ StringLengthCounter.walkBitwiseAndExpr(bandExpr, counts);
131
125
  }
132
126
  }
133
127
 
134
- private walkBitwiseAndExpr(
128
+ private static walkBitwiseAndExpr(
135
129
  ctx: Parser.BitwiseAndExpressionContext,
136
130
  counts: Map<string, number>,
137
131
  ): void {
138
132
  for (const shiftExpr of ctx.shiftExpression()) {
139
- this.walkShiftExpr(shiftExpr, counts);
133
+ StringLengthCounter.walkShiftExpr(shiftExpr, counts);
140
134
  }
141
135
  }
142
136
 
143
- private walkShiftExpr(
137
+ private static walkShiftExpr(
144
138
  ctx: Parser.ShiftExpressionContext,
145
139
  counts: Map<string, number>,
146
140
  ): void {
147
141
  for (const addExpr of ctx.additiveExpression()) {
148
- this.walkAdditiveExpr(addExpr, counts);
142
+ StringLengthCounter.walkAdditiveExpr(addExpr, counts);
149
143
  }
150
144
  }
151
145
 
152
- private walkAdditiveExpr(
146
+ private static walkAdditiveExpr(
153
147
  ctx: Parser.AdditiveExpressionContext,
154
148
  counts: Map<string, number>,
155
149
  ): void {
156
150
  for (const multExpr of ctx.multiplicativeExpression()) {
157
- this.walkMultiplicativeExpr(multExpr, counts);
151
+ StringLengthCounter.walkMultiplicativeExpr(multExpr, counts);
158
152
  }
159
153
  }
160
154
 
161
- private walkMultiplicativeExpr(
155
+ private static walkMultiplicativeExpr(
162
156
  ctx: Parser.MultiplicativeExpressionContext,
163
157
  counts: Map<string, number>,
164
158
  ): void {
165
159
  for (const unaryExpr of ctx.unaryExpression()) {
166
- this.walkUnaryExpr(unaryExpr, counts);
160
+ StringLengthCounter.walkUnaryExpr(unaryExpr, counts);
167
161
  }
168
162
  }
169
163
 
170
- private walkUnaryExpr(
164
+ private static walkUnaryExpr(
171
165
  ctx: Parser.UnaryExpressionContext,
172
166
  counts: Map<string, number>,
173
167
  ): void {
174
168
  const postfix = ctx.postfixExpression();
175
169
  if (postfix) {
176
- this.walkPostfixExpr(postfix, counts);
170
+ StringLengthCounter.walkPostfixExpr(postfix, counts);
177
171
  }
178
172
  // Also check nested unary expressions
179
173
  const nestedUnary = ctx.unaryExpression();
180
174
  if (nestedUnary) {
181
- this.walkUnaryExpr(nestedUnary, counts);
175
+ StringLengthCounter.walkUnaryExpr(nestedUnary, counts);
182
176
  }
183
177
  }
184
178
 
185
179
  /**
186
180
  * Walk a postfix expression - this is where we detect .length accesses.
187
181
  */
188
- private walkPostfixExpr(
182
+ private static walkPostfixExpr(
189
183
  ctx: Parser.PostfixExpressionContext,
190
184
  counts: Map<string, number>,
191
185
  ): void {
@@ -199,7 +193,7 @@ class StringLengthCounter {
199
193
  const memberName = op.IDENTIFIER()?.getText();
200
194
  if (memberName === "length") {
201
195
  // Check if this is a string type
202
- const typeInfo = this.typeRegistry(primaryId);
196
+ const typeInfo = CodeGenState.typeRegistry.get(primaryId);
203
197
  if (typeInfo?.isString) {
204
198
  const currentCount = counts.get(primaryId) || 0;
205
199
  counts.set(primaryId, currentCount + 1);
@@ -207,51 +201,54 @@ class StringLengthCounter {
207
201
  }
208
202
  // Walk any nested expressions in array accesses or function calls
209
203
  for (const expr of op.expression()) {
210
- this.walkExpression(expr, counts);
204
+ StringLengthCounter.walkExpression(expr, counts);
211
205
  }
212
206
  }
213
207
  }
214
208
 
215
209
  // Walk nested expression in primary if present
216
210
  if (primary.expression()) {
217
- this.walkExpression(primary.expression()!, counts);
211
+ StringLengthCounter.walkExpression(primary.expression()!, counts);
218
212
  }
219
213
  }
220
214
 
221
215
  /**
222
216
  * Walk a statement, counting .length accesses.
223
217
  */
224
- private walkStatement(
218
+ private static walkStatement(
225
219
  ctx: Parser.StatementContext,
226
220
  counts: Map<string, number>,
227
221
  ): void {
228
222
  // Assignment statement
229
223
  if (ctx.assignmentStatement()) {
230
224
  const assign = ctx.assignmentStatement()!;
231
- // Count in target (array index expressions)
225
+ // Count in target (array index expressions from postfix ops)
232
226
  const target = assign.assignmentTarget();
233
- if (target.arrayAccess()) {
234
- for (const expr of target.arrayAccess()!.expression()) {
235
- this.walkExpression(expr, counts);
227
+ for (const op of target.postfixTargetOp()) {
228
+ for (const expr of op.expression()) {
229
+ StringLengthCounter.walkExpression(expr, counts);
236
230
  }
237
231
  }
238
232
  // Count in value expression
239
- this.walkExpression(assign.expression(), counts);
233
+ StringLengthCounter.walkExpression(assign.expression(), counts);
240
234
  }
241
235
  // Expression statement
242
236
  if (ctx.expressionStatement()) {
243
- this.walkExpression(ctx.expressionStatement()!.expression(), counts);
237
+ StringLengthCounter.walkExpression(
238
+ ctx.expressionStatement()!.expression(),
239
+ counts,
240
+ );
244
241
  }
245
242
  // Variable declaration
246
243
  if (ctx.variableDeclaration()) {
247
244
  const varDecl = ctx.variableDeclaration()!;
248
245
  if (varDecl.expression()) {
249
- this.walkExpression(varDecl.expression()!, counts);
246
+ StringLengthCounter.walkExpression(varDecl.expression()!, counts);
250
247
  }
251
248
  }
252
249
  // Nested block
253
250
  if (ctx.block()) {
254
- this.countBlockInto(ctx.block()!, counts);
251
+ StringLengthCounter.countBlockInto(ctx.block()!, counts);
255
252
  }
256
253
  // Note: Could add recursion for if/while/for bodies if deeper analysis needed
257
254
  }