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.
- package/README.md +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- 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 +1410 -2587
- 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 +2082 -52
- 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 +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- 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/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- 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/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- 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/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -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__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- 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/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- 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
|
|
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
|
-
*
|
|
32
|
+
* Callback type for generating expression code.
|
|
32
33
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
*
|
|
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(
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
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
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
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,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
|
-
|
|
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 {
|
|
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
|
-
|
|
234
|
-
for (const expr of
|
|
235
|
-
|
|
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
|
-
|
|
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
|
}
|