c-next 0.1.62 → 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 (55) hide show
  1. package/README.md +86 -63
  2. package/package.json +1 -1
  3. package/src/transpiler/Transpiler.ts +3 -2
  4. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
  6. package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
  7. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  8. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  9. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  10. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
  12. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  13. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  14. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
  16. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  20. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
  21. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
  22. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  24. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
  25. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
  26. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  27. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  28. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  29. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
  30. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  31. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  33. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  34. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  35. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
  36. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
  37. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  38. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  40. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  41. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  42. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  43. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  44. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  45. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  46. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  47. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  50. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  51. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  52. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  53. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  54. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  55. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
@@ -7,10 +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";
15
+ import CodeGenState from "../CodeGenState.js";
14
16
 
15
17
  /**
16
18
  * Result of analyzing a member chain for bit access.
@@ -27,20 +29,9 @@ interface IBitAccessAnalysisResult {
27
29
  }
28
30
 
29
31
  /**
30
- * Dependencies required for member chain analysis.
32
+ * Callback type for generating expression code.
31
33
  */
32
- interface IMemberChainAnalyzerDeps {
33
- /** Type registry for looking up variable types */
34
- typeRegistry: ReadonlyMap<string, TTypeInfo>;
35
- /** Struct field types by struct name */
36
- structFields: ReadonlyMap<string, ReadonlyMap<string, string>>;
37
- /** Struct field array flags by struct name */
38
- structFieldArrays: ReadonlyMap<string, ReadonlySet<string>>;
39
- /** Function to check if a type name is a known struct */
40
- isKnownStruct: (name: string) => boolean;
41
- /** Function to generate expression code */
42
- generateExpression: (ctx: Parser.ExpressionContext) => string;
43
- }
34
+ type GenerateExpressionFn = (ctx: Parser.ExpressionContext) => string;
44
35
 
45
36
  /** Mutable state for tracking types through a member chain. */
46
37
  interface IChainState {
@@ -57,12 +48,6 @@ interface IChainState {
57
48
  * detection callbacks to determine if the final subscript is bit access.
58
49
  */
59
50
  class MemberChainAnalyzer {
60
- private readonly deps: IMemberChainAnalyzerDeps;
61
-
62
- constructor(deps: IMemberChainAnalyzerDeps) {
63
- this.deps = deps;
64
- }
65
-
66
51
  /**
67
52
  * Analyze a member chain target to detect bit access at the end.
68
53
  *
@@ -70,9 +55,13 @@ class MemberChainAnalyzer {
70
55
  * Uses direct postfixTargetOp analysis with type tracking.
71
56
  *
72
57
  * @param targetCtx - The assignment target context to analyze
58
+ * @param generateExpression - Callback to generate expression code
73
59
  * @returns Analysis result with bit access information
74
60
  */
75
- analyze(targetCtx: Parser.AssignmentTargetContext): IBitAccessAnalysisResult {
61
+ static analyze(
62
+ targetCtx: Parser.AssignmentTargetContext,
63
+ generateExpression: GenerateExpressionFn,
64
+ ): IBitAccessAnalysisResult {
76
65
  const baseId = targetCtx.IDENTIFIER()?.getText();
77
66
  const postfixOps = targetCtx.postfixTargetOp();
78
67
 
@@ -94,7 +83,7 @@ class MemberChainAnalyzer {
94
83
  ).length;
95
84
 
96
85
  // Walk through the chain to find the type and array status before the last subscript
97
- const targetInfo = this.resolveTargetTypeAndArrayStatus(
86
+ const targetInfo = MemberChainAnalyzer.resolveTargetTypeAndArrayStatus(
98
87
  baseId,
99
88
  postfixOps.slice(0, -1),
100
89
  subscriptCount - 1, // subscripts before the last one
@@ -109,13 +98,17 @@ class MemberChainAnalyzer {
109
98
  }
110
99
 
111
100
  // Check if the type is an integer (bit access only works on integers)
112
- if (!this.isIntegerType(targetInfo.type)) {
101
+ if (!MemberChainAnalyzer.isIntegerType(targetInfo.type)) {
113
102
  return { isBitAccess: false };
114
103
  }
115
104
 
116
105
  // Build the base target expression (everything except the last subscript)
117
- const baseTarget = this.buildBaseTarget(baseId, postfixOps.slice(0, -1));
118
- const bitIndex = this.deps.generateExpression(lastExprs[0]);
106
+ const baseTarget = MemberChainAnalyzer.buildBaseTarget(
107
+ baseId,
108
+ postfixOps.slice(0, -1),
109
+ generateExpression,
110
+ );
111
+ const bitIndex = generateExpression(lastExprs[0]);
119
112
 
120
113
  return {
121
114
  isBitAccess: true,
@@ -129,19 +122,19 @@ class MemberChainAnalyzer {
129
122
  * Resolve the type and array status of the target by walking through postfix operations.
130
123
  * Returns the type and whether it's still an array before the last subscript.
131
124
  */
132
- private resolveTargetTypeAndArrayStatus(
125
+ private static resolveTargetTypeAndArrayStatus(
133
126
  baseId: string,
134
127
  ops: Parser.PostfixTargetOpContext[],
135
128
  _subscriptsSoFar: number,
136
129
  ): { type: string; isArray: boolean } | undefined {
137
- const baseTypeInfo = this.deps.typeRegistry.get(baseId);
130
+ const baseTypeInfo = CodeGenState.typeRegistry.get(baseId);
138
131
  if (!baseTypeInfo) {
139
132
  return undefined;
140
133
  }
141
134
 
142
135
  const state: IChainState = {
143
136
  currentType: baseTypeInfo.baseType,
144
- currentStructType: this.deps.isKnownStruct(baseTypeInfo.baseType)
137
+ currentStructType: CodeGenState.isKnownStruct(baseTypeInfo.baseType)
145
138
  ? baseTypeInfo.baseType
146
139
  : undefined,
147
140
  isCurrentArray: baseTypeInfo.isArray,
@@ -151,12 +144,12 @@ class MemberChainAnalyzer {
151
144
  for (let i = 0; i < ops.length; i++) {
152
145
  const op = ops[i];
153
146
  if (op.IDENTIFIER()) {
154
- const result = this.processMemberOp(op, ops, i, state);
147
+ const result = MemberChainAnalyzer.processMemberOp(op, ops, i, state);
155
148
  if (!result) {
156
149
  return undefined;
157
150
  }
158
151
  } else {
159
- this.processSubscriptOp(state);
152
+ MemberChainAnalyzer.processSubscriptOp(state);
160
153
  }
161
154
  }
162
155
 
@@ -167,7 +160,7 @@ class MemberChainAnalyzer {
167
160
  * Process a member access operation (.fieldName) and update chain state.
168
161
  * Returns false if the access is invalid.
169
162
  */
170
- private processMemberOp(
163
+ private static processMemberOp(
171
164
  op: Parser.PostfixTargetOpContext,
172
165
  ops: Parser.PostfixTargetOpContext[],
173
166
  opIndex: number,
@@ -178,7 +171,9 @@ class MemberChainAnalyzer {
178
171
  return false;
179
172
  }
180
173
 
181
- const structFields = this.deps.structFields.get(state.currentStructType);
174
+ const structFields = CodeGenState.symbols?.structFields.get(
175
+ state.currentStructType,
176
+ );
182
177
  if (!structFields) {
183
178
  return false;
184
179
  }
@@ -191,19 +186,19 @@ class MemberChainAnalyzer {
191
186
  state.currentType = fieldType;
192
187
 
193
188
  // Check if this field is an array
194
- const arrayFields = this.deps.structFieldArrays.get(
189
+ const arrayFields = CodeGenState.symbols?.structFieldArrays.get(
195
190
  state.currentStructType,
196
191
  );
197
192
  state.isCurrentArray = arrayFields?.has(fieldName) ?? false;
198
193
 
199
194
  // If the field type is a struct, update currentStructType
200
- state.currentStructType = this.deps.isKnownStruct(state.currentType)
195
+ state.currentStructType = CodeGenState.isKnownStruct(state.currentType)
201
196
  ? state.currentType
202
197
  : undefined;
203
198
 
204
199
  // Calculate array dimensions remaining based on remaining subscripts
205
200
  state.arrayDimsRemaining = state.isCurrentArray
206
- ? this.countRemainingSubscripts(ops, opIndex) + 1
201
+ ? MemberChainAnalyzer.countRemainingSubscripts(ops, opIndex) + 1
207
202
  : 0;
208
203
 
209
204
  return true;
@@ -212,7 +207,7 @@ class MemberChainAnalyzer {
212
207
  /**
213
208
  * Count remaining subscript operations after the given index.
214
209
  */
215
- private countRemainingSubscripts(
210
+ private static countRemainingSubscripts(
216
211
  ops: Parser.PostfixTargetOpContext[],
217
212
  afterIndex: number,
218
213
  ): number {
@@ -224,7 +219,7 @@ class MemberChainAnalyzer {
224
219
  /**
225
220
  * Process a subscript operation ([expr]) and update chain state.
226
221
  */
227
- private processSubscriptOp(state: IChainState): void {
222
+ private static processSubscriptOp(state: IChainState): void {
228
223
  if (!state.isCurrentArray || state.arrayDimsRemaining <= 0) {
229
224
  return;
230
225
  }
@@ -232,7 +227,7 @@ class MemberChainAnalyzer {
232
227
  state.arrayDimsRemaining--;
233
228
  if (state.arrayDimsRemaining === 0) {
234
229
  state.isCurrentArray = false;
235
- state.currentStructType = this.deps.isKnownStruct(state.currentType)
230
+ state.currentStructType = CodeGenState.isKnownStruct(state.currentType)
236
231
  ? state.currentType
237
232
  : undefined;
238
233
  }
@@ -241,7 +236,7 @@ class MemberChainAnalyzer {
241
236
  /**
242
237
  * Check if a type is an integer type.
243
238
  */
244
- private isIntegerType(typeName: string): boolean {
239
+ private static isIntegerType(typeName: string): boolean {
245
240
  const intTypes = new Set([
246
241
  "u8",
247
242
  "u16",
@@ -258,9 +253,10 @@ class MemberChainAnalyzer {
258
253
  /**
259
254
  * Build the target expression string from base identifier and postfix operations.
260
255
  */
261
- private buildBaseTarget(
256
+ private static buildBaseTarget(
262
257
  baseId: string,
263
258
  ops: Parser.PostfixTargetOpContext[],
259
+ generateExpression: GenerateExpressionFn,
264
260
  ): string {
265
261
  let result = baseId;
266
262
 
@@ -270,13 +266,13 @@ class MemberChainAnalyzer {
270
266
  } else {
271
267
  const exprs = op.expression();
272
268
  if (exprs.length === 1) {
273
- result += "[" + this.deps.generateExpression(exprs[0]) + "]";
269
+ result += "[" + generateExpression(exprs[0]) + "]";
274
270
  } else if (exprs.length === 2) {
275
271
  result +=
276
272
  "[" +
277
- this.deps.generateExpression(exprs[0]) +
273
+ generateExpression(exprs[0]) +
278
274
  ", " +
279
- this.deps.generateExpression(exprs[1]) +
275
+ generateExpression(exprs[1]) +
280
276
  "]";
281
277
  }
282
278
  }
@@ -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,21 +201,21 @@ 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 {
@@ -232,26 +226,29 @@ class StringLengthCounter {
232
226
  const target = assign.assignmentTarget();
233
227
  for (const op of target.postfixTargetOp()) {
234
228
  for (const expr of op.expression()) {
235
- this.walkExpression(expr, counts);
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
  }