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.
- package/README.md +86 -63
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +3 -2
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- 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
|
|
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
|
-
*
|
|
32
|
+
* Callback type for generating expression code.
|
|
31
33
|
*/
|
|
32
|
-
|
|
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(
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
118
|
-
|
|
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 =
|
|
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:
|
|
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 =
|
|
147
|
+
const result = MemberChainAnalyzer.processMemberOp(op, ops, i, state);
|
|
155
148
|
if (!result) {
|
|
156
149
|
return undefined;
|
|
157
150
|
}
|
|
158
151
|
} else {
|
|
159
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
?
|
|
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 =
|
|
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 += "[" +
|
|
269
|
+
result += "[" + generateExpression(exprs[0]) + "]";
|
|
274
270
|
} else if (exprs.length === 2) {
|
|
275
271
|
result +=
|
|
276
272
|
"[" +
|
|
277
|
-
|
|
273
|
+
generateExpression(exprs[0]) +
|
|
278
274
|
", " +
|
|
279
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
42
|
+
static countBlockInto(
|
|
43
|
+
ctx: Parser.BlockContext,
|
|
44
|
+
counts: Map<string, number>,
|
|
45
|
+
): void {
|
|
52
46
|
for (const stmt of ctx.statement()) {
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
229
|
+
StringLengthCounter.walkExpression(expr, counts);
|
|
236
230
|
}
|
|
237
231
|
}
|
|
238
232
|
// Count in value expression
|
|
239
|
-
|
|
233
|
+
StringLengthCounter.walkExpression(assign.expression(), counts);
|
|
240
234
|
}
|
|
241
235
|
// Expression statement
|
|
242
236
|
if (ctx.expressionStatement()) {
|
|
243
|
-
|
|
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
|
-
|
|
246
|
+
StringLengthCounter.walkExpression(varDecl.expression()!, counts);
|
|
250
247
|
}
|
|
251
248
|
}
|
|
252
249
|
// Nested block
|
|
253
250
|
if (ctx.block()) {
|
|
254
|
-
|
|
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
|
}
|