c-next 0.1.48 → 0.1.50
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/package.json +1 -1
- package/src/codegen/CodeGenerator.ts +179 -41
- package/src/pipeline/Pipeline.ts +94 -0
- package/src/project/Project.ts +31 -0
package/package.json
CHANGED
|
@@ -189,7 +189,7 @@ interface GeneratorContext {
|
|
|
189
189
|
indentLevel: number;
|
|
190
190
|
scopeMembers: Map<string, Set<string>>; // scope -> member names (ADR-016)
|
|
191
191
|
currentParameters: Map<string, TParameterInfo>; // ADR-006: track params for pointer semantics
|
|
192
|
-
|
|
192
|
+
// Issue #558: modifiedParameters removed - now uses analysis-phase results from this.modifiedParameters
|
|
193
193
|
localArrays: Set<string>; // ADR-006: track local array variables (no & needed)
|
|
194
194
|
localVariables: Set<string>; // ADR-016: track local variables (allowed as bare identifiers)
|
|
195
195
|
floatBitShadows: Set<string>; // Track declared shadow variables for float bit indexing
|
|
@@ -221,7 +221,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
221
221
|
indentLevel: 0,
|
|
222
222
|
scopeMembers: new Map(), // ADR-016: renamed from namespaceMembers
|
|
223
223
|
currentParameters: new Map(),
|
|
224
|
-
|
|
224
|
+
// Issue #558: modifiedParameters removed - now uses analysis-phase results
|
|
225
225
|
localArrays: new Set(),
|
|
226
226
|
localVariables: new Set(), // ADR-016: track local variables
|
|
227
227
|
floatBitShadows: new Set(), // Track declared shadow variables for float bit indexing
|
|
@@ -327,6 +327,17 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
327
327
|
*/
|
|
328
328
|
private readonly modifiedParameters: Map<string, Set<string>> = new Map();
|
|
329
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Issue #558: Pending cross-file modifications to inject after analyzePassByValue clears.
|
|
332
|
+
* Set by Pipeline before generate() to share modifications from previously processed files.
|
|
333
|
+
*/
|
|
334
|
+
private pendingCrossFileModifications: Map<string, Set<string>> | null = null;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Issue #558: Pending cross-file parameter lists to inject for transitive propagation.
|
|
338
|
+
*/
|
|
339
|
+
private pendingCrossFileParamLists: Map<string, string[]> | null = null;
|
|
340
|
+
|
|
330
341
|
/**
|
|
331
342
|
* Issue #269: Tracks which parameters should pass by value
|
|
332
343
|
* Map of functionName -> Set of passByValue parameter names
|
|
@@ -1061,7 +1072,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1061
1072
|
this.context.localVariables.clear();
|
|
1062
1073
|
this.context.floatBitShadows.clear();
|
|
1063
1074
|
this.context.floatShadowCurrent.clear();
|
|
1064
|
-
|
|
1075
|
+
// Issue #558: modifiedParameters tracking removed - uses analysis-phase results
|
|
1065
1076
|
this.context.inFunctionBody = true;
|
|
1066
1077
|
}
|
|
1067
1078
|
|
|
@@ -1106,13 +1117,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1106
1117
|
|
|
1107
1118
|
/**
|
|
1108
1119
|
* Issue #268: Update symbol parameters with auto-const info.
|
|
1109
|
-
*
|
|
1120
|
+
* Issue #558: Now uses analysis-phase results for modification tracking.
|
|
1110
1121
|
*/
|
|
1111
1122
|
updateFunctionParamsAutoConst(functionName: string): void {
|
|
1112
|
-
// Collect unmodified parameters for this function
|
|
1123
|
+
// Collect unmodified parameters for this function using analysis results
|
|
1113
1124
|
const unmodifiedParams = new Set<string>();
|
|
1125
|
+
const modifiedSet = this.modifiedParameters.get(functionName);
|
|
1114
1126
|
for (const [paramName] of this.context.currentParameters) {
|
|
1115
|
-
if (!
|
|
1127
|
+
if (!modifiedSet?.has(paramName)) {
|
|
1116
1128
|
unmodifiedParams.add(paramName);
|
|
1117
1129
|
}
|
|
1118
1130
|
}
|
|
@@ -1121,11 +1133,100 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1121
1133
|
|
|
1122
1134
|
/**
|
|
1123
1135
|
* Issue #268: Mark a parameter as modified for auto-const tracking.
|
|
1136
|
+
* Issue #558: Now a no-op - analysis phase handles all modification tracking
|
|
1137
|
+
* including transitive propagation across function calls and files.
|
|
1138
|
+
*/
|
|
1139
|
+
markParameterModified(_paramName: string): void {
|
|
1140
|
+
// No-op: Analysis phase (analyzePassByValue) now handles all modification
|
|
1141
|
+
// tracking including cross-file and transitive propagation.
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Issue #558: Check if a parameter is modified using analysis-phase results.
|
|
1146
|
+
* This is the unified source of truth for modification tracking.
|
|
1147
|
+
*/
|
|
1148
|
+
private _isCurrentParameterModified(paramName: string): boolean {
|
|
1149
|
+
const funcName = this.context.currentFunctionName;
|
|
1150
|
+
if (!funcName) return false;
|
|
1151
|
+
return this.modifiedParameters.get(funcName)?.has(paramName) ?? false;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* Issue #558: Get the modified parameters map for cross-file propagation.
|
|
1156
|
+
* Returns function name -> set of modified parameter names.
|
|
1157
|
+
*/
|
|
1158
|
+
getModifiedParameters(): ReadonlyMap<string, Set<string>> {
|
|
1159
|
+
return this.modifiedParameters;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* Issue #558: Set cross-file modification data to inject during analyzePassByValue.
|
|
1164
|
+
* Called by Pipeline before generate() to share modifications from previously processed files.
|
|
1165
|
+
*/
|
|
1166
|
+
setCrossFileModifications(
|
|
1167
|
+
modifications: Map<string, Set<string>>,
|
|
1168
|
+
paramLists: Map<string, string[]>,
|
|
1169
|
+
): void {
|
|
1170
|
+
this.pendingCrossFileModifications = modifications;
|
|
1171
|
+
this.pendingCrossFileParamLists = paramLists;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* Issue #558: Get the function parameter lists for cross-file propagation.
|
|
1176
|
+
*/
|
|
1177
|
+
getFunctionParamLists(): ReadonlyMap<string, string[]> {
|
|
1178
|
+
return this.functionParamLists;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* Issue #561: Analyze modifications in a parse tree without full code generation.
|
|
1183
|
+
* Used by Pipeline.transpileSource() to collect modification info from includes
|
|
1184
|
+
* for cross-file const inference (unified with Pipeline.run() behavior).
|
|
1185
|
+
*
|
|
1186
|
+
* Returns the modifications and param lists discovered in this tree.
|
|
1124
1187
|
*/
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1188
|
+
analyzeModificationsOnly(tree: Parser.ProgramContext): {
|
|
1189
|
+
modifications: Map<string, Set<string>>;
|
|
1190
|
+
paramLists: Map<string, string[]>;
|
|
1191
|
+
} {
|
|
1192
|
+
// Save current state
|
|
1193
|
+
const savedModifications = new Map(this.modifiedParameters);
|
|
1194
|
+
const savedParamLists = new Map(this.functionParamLists);
|
|
1195
|
+
const savedCallGraph = new Map(this.functionCallGraph);
|
|
1196
|
+
|
|
1197
|
+
// Clear for fresh analysis
|
|
1198
|
+
this.modifiedParameters.clear();
|
|
1199
|
+
this.functionParamLists.clear();
|
|
1200
|
+
this.functionCallGraph.clear();
|
|
1201
|
+
|
|
1202
|
+
// Run modification analysis on the tree
|
|
1203
|
+
this.collectFunctionParametersAndModifications(tree);
|
|
1204
|
+
|
|
1205
|
+
// Capture results before restoring state
|
|
1206
|
+
const modifications = new Map<string, Set<string>>();
|
|
1207
|
+
for (const [funcName, params] of this.modifiedParameters) {
|
|
1208
|
+
modifications.set(funcName, new Set(params));
|
|
1209
|
+
}
|
|
1210
|
+
const paramLists = new Map<string, string[]>();
|
|
1211
|
+
for (const [funcName, params] of this.functionParamLists) {
|
|
1212
|
+
paramLists.set(funcName, [...params]);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Restore previous state by clearing and repopulating (readonly maps)
|
|
1216
|
+
this.modifiedParameters.clear();
|
|
1217
|
+
for (const [k, v] of savedModifications) {
|
|
1218
|
+
this.modifiedParameters.set(k, v);
|
|
1128
1219
|
}
|
|
1220
|
+
this.functionParamLists.clear();
|
|
1221
|
+
for (const [k, v] of savedParamLists) {
|
|
1222
|
+
this.functionParamLists.set(k, v);
|
|
1223
|
+
}
|
|
1224
|
+
this.functionCallGraph.clear();
|
|
1225
|
+
for (const [k, v] of savedCallGraph) {
|
|
1226
|
+
this.functionCallGraph.set(k, v);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
return { modifications, paramLists };
|
|
1129
1230
|
}
|
|
1130
1231
|
|
|
1131
1232
|
/**
|
|
@@ -1539,7 +1640,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1539
1640
|
indentLevel: 0,
|
|
1540
1641
|
scopeMembers: new Map(), // ADR-016
|
|
1541
1642
|
currentParameters: new Map(),
|
|
1542
|
-
|
|
1643
|
+
// Issue #558: modifiedParameters removed - uses analysis-phase results
|
|
1543
1644
|
localArrays: new Set(),
|
|
1544
1645
|
localVariables: new Set(), // ADR-016
|
|
1545
1646
|
floatBitShadows: new Set(), // Track declared shadow variables for float bit indexing
|
|
@@ -2025,6 +2126,29 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2025
2126
|
// Phase 1: Collect function parameter lists and direct modifications
|
|
2026
2127
|
this.collectFunctionParametersAndModifications(tree);
|
|
2027
2128
|
|
|
2129
|
+
// Issue #558: Inject cross-file data before transitive propagation
|
|
2130
|
+
if (this.pendingCrossFileModifications) {
|
|
2131
|
+
for (const [funcName, params] of this.pendingCrossFileModifications) {
|
|
2132
|
+
const existing = this.modifiedParameters.get(funcName);
|
|
2133
|
+
if (existing) {
|
|
2134
|
+
for (const param of params) {
|
|
2135
|
+
existing.add(param);
|
|
2136
|
+
}
|
|
2137
|
+
} else {
|
|
2138
|
+
this.modifiedParameters.set(funcName, new Set(params));
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
this.pendingCrossFileModifications = null; // Clear after use
|
|
2142
|
+
}
|
|
2143
|
+
if (this.pendingCrossFileParamLists) {
|
|
2144
|
+
for (const [funcName, params] of this.pendingCrossFileParamLists) {
|
|
2145
|
+
if (!this.functionParamLists.has(funcName)) {
|
|
2146
|
+
this.functionParamLists.set(funcName, params);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
this.pendingCrossFileParamLists = null; // Clear after use
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2028
2152
|
// Phase 2: Fixed-point iteration for transitive modifications
|
|
2029
2153
|
this.propagateTransitiveModifications();
|
|
2030
2154
|
|
|
@@ -2120,17 +2244,31 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2120
2244
|
// Check for assignment statements
|
|
2121
2245
|
if (stmt.assignmentStatement()) {
|
|
2122
2246
|
const assign = stmt.assignmentStatement()!;
|
|
2123
|
-
// Get the target - use assignmentTarget() which has IDENTIFIER()
|
|
2124
2247
|
const target = assign.assignmentTarget();
|
|
2248
|
+
|
|
2249
|
+
// Issue #558: Extract base identifier from assignment target
|
|
2250
|
+
// - Simple identifier: x <- value
|
|
2251
|
+
// - Member access: x.field <- value (first IDENTIFIER is the base)
|
|
2252
|
+
// - Array access: x[i] <- value
|
|
2253
|
+
let baseIdentifier: string | null = null;
|
|
2254
|
+
|
|
2125
2255
|
if (target?.IDENTIFIER()) {
|
|
2126
|
-
// Simple identifier assignment
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2256
|
+
// Simple identifier assignment
|
|
2257
|
+
baseIdentifier = target.IDENTIFIER()!.getText();
|
|
2258
|
+
} else if (target?.memberAccess()) {
|
|
2259
|
+
// Member access: first IDENTIFIER is the base (e.g., cfg.value -> cfg)
|
|
2260
|
+
const identifiers = target.memberAccess()!.IDENTIFIER();
|
|
2261
|
+
if (identifiers.length > 0) {
|
|
2262
|
+
baseIdentifier = identifiers[0].getText();
|
|
2133
2263
|
}
|
|
2264
|
+
} else if (target?.arrayAccess()) {
|
|
2265
|
+
// Array access: IDENTIFIER is the base (e.g., arr[0] -> arr)
|
|
2266
|
+
baseIdentifier = target.arrayAccess()!.IDENTIFIER()?.getText() ?? null;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
if (baseIdentifier && paramSet.has(baseIdentifier)) {
|
|
2270
|
+
// Direct or member/array assignment modifies the parameter
|
|
2271
|
+
this.modifiedParameters.get(funcName)!.add(baseIdentifier);
|
|
2134
2272
|
}
|
|
2135
2273
|
}
|
|
2136
2274
|
|
|
@@ -2386,9 +2524,17 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2386
2524
|
const memberNames: string[] = [];
|
|
2387
2525
|
|
|
2388
2526
|
// Start with primary identifier if it's a scope name (not 'global' or 'this')
|
|
2527
|
+
// Issue #561: When 'this' is used, resolve to the current scope name from funcName
|
|
2389
2528
|
const primaryId = primary.IDENTIFIER()?.getText();
|
|
2390
|
-
if (primaryId && primaryId !== "global"
|
|
2529
|
+
if (primaryId && primaryId !== "global") {
|
|
2391
2530
|
memberNames.push(primaryId);
|
|
2531
|
+
} else if (primary.THIS()) {
|
|
2532
|
+
// Issue #561: 'this' keyword - resolve to current scope name from funcName
|
|
2533
|
+
// funcName format: "ScopeName_methodName" -> extract "ScopeName"
|
|
2534
|
+
const scopeName = funcName.split("_")[0];
|
|
2535
|
+
if (scopeName && scopeName !== funcName) {
|
|
2536
|
+
memberNames.push(scopeName);
|
|
2537
|
+
}
|
|
2392
2538
|
}
|
|
2393
2539
|
|
|
2394
2540
|
// Collect member access names until we hit a function call
|
|
@@ -5358,8 +5504,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5358
5504
|
// Track parameters for ADR-006 pointer semantics
|
|
5359
5505
|
this._setParameters(ctx.parameterList() ?? null);
|
|
5360
5506
|
|
|
5361
|
-
// Issue #
|
|
5362
|
-
this.context.modifiedParameters.clear();
|
|
5507
|
+
// Issue #558: modifiedParameters tracking removed - uses analysis-phase results
|
|
5363
5508
|
|
|
5364
5509
|
// ADR-016: Clear local variables and mark that we're in a function body
|
|
5365
5510
|
this.context.localVariables.clear();
|
|
@@ -5525,8 +5670,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5525
5670
|
// Arrays pass naturally as pointers
|
|
5526
5671
|
if (dims.length > 0) {
|
|
5527
5672
|
const dimStr = dims.map((d) => this._generateArrayDimension(d)).join("");
|
|
5528
|
-
// Issue #268: Add const for unmodified array parameters
|
|
5529
|
-
const wasModified = this.
|
|
5673
|
+
// Issue #268/#558: Add const for unmodified array parameters (uses analysis results)
|
|
5674
|
+
const wasModified = this._isCurrentParameterModified(name);
|
|
5530
5675
|
const autoConst = !wasModified && !constMod ? "const " : "";
|
|
5531
5676
|
return `${autoConst}${constMod}${type} ${name}${dimStr}`;
|
|
5532
5677
|
}
|
|
@@ -5557,8 +5702,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5557
5702
|
// ADR-045: String parameters (non-array) are passed as char*
|
|
5558
5703
|
// Issue #551: Handle before unknown type check
|
|
5559
5704
|
if (ctx.type().stringType() && dims.length === 0) {
|
|
5560
|
-
// Issue #268: Add const for unmodified string parameters
|
|
5561
|
-
const wasModified = this.
|
|
5705
|
+
// Issue #268/#558: Add const for unmodified string parameters (uses analysis results)
|
|
5706
|
+
const wasModified = this._isCurrentParameterModified(name);
|
|
5562
5707
|
const autoConst = !wasModified && !constMod ? "const " : "";
|
|
5563
5708
|
return `${autoConst}${constMod}char* ${name}`;
|
|
5564
5709
|
}
|
|
@@ -5566,8 +5711,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
5566
5711
|
// ADR-006: Pass by reference for known struct types and known primitives
|
|
5567
5712
|
// Issue #551: Unknown types (external enums, typedefs) use pass-by-value
|
|
5568
5713
|
if (this._isKnownStruct(typeName) || this._isKnownPrimitive(typeName)) {
|
|
5569
|
-
// Issue #268: Add const for unmodified pointer parameters
|
|
5570
|
-
const wasModified = this.
|
|
5714
|
+
// Issue #268/#558: Add const for unmodified pointer parameters (uses analysis results)
|
|
5715
|
+
const wasModified = this._isCurrentParameterModified(name);
|
|
5571
5716
|
const autoConst = !wasModified && !constMod ? "const " : "";
|
|
5572
5717
|
// Issue #409: In C++ mode, use references (&) instead of pointers (*)
|
|
5573
5718
|
// This allows C-Next callbacks to match C++ function pointer signatures
|
|
@@ -6833,10 +6978,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6833
6978
|
throw new Error(constError);
|
|
6834
6979
|
}
|
|
6835
6980
|
|
|
6836
|
-
// Issue #
|
|
6837
|
-
if (this.context.currentParameters.has(id)) {
|
|
6838
|
-
this.context.modifiedParameters.add(id);
|
|
6839
|
-
}
|
|
6981
|
+
// Issue #558: Parameter modification tracking removed - uses analysis-phase results
|
|
6840
6982
|
|
|
6841
6983
|
// Invalidate float shadow when variable is assigned directly
|
|
6842
6984
|
const shadowName = `__bits_${id}`;
|
|
@@ -6962,10 +7104,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6962
7104
|
);
|
|
6963
7105
|
}
|
|
6964
7106
|
|
|
6965
|
-
// Issue #
|
|
6966
|
-
if (this.context.currentParameters.has(arrayName)) {
|
|
6967
|
-
this.context.modifiedParameters.add(arrayName);
|
|
6968
|
-
}
|
|
7107
|
+
// Issue #558: Parameter modification tracking removed - uses analysis-phase results
|
|
6969
7108
|
}
|
|
6970
7109
|
|
|
6971
7110
|
// Check member access on const struct - validate the root is not const
|
|
@@ -6978,10 +7117,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6978
7117
|
throw new Error(`${constError} (member access)`);
|
|
6979
7118
|
}
|
|
6980
7119
|
|
|
6981
|
-
// Issue #
|
|
6982
|
-
if (this.context.currentParameters.has(rootName)) {
|
|
6983
|
-
this.context.modifiedParameters.add(rootName);
|
|
6984
|
-
}
|
|
7120
|
+
// Issue #558: Parameter modification tracking removed - uses analysis-phase results
|
|
6985
7121
|
|
|
6986
7122
|
// ADR-013: Check for read-only register members (ro = implicitly const)
|
|
6987
7123
|
if (identifiers.length >= 2) {
|
|
@@ -7124,7 +7260,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
7124
7260
|
!paramInfo.isStruct &&
|
|
7125
7261
|
this._isKnownPrimitive(paramInfo.baseType)
|
|
7126
7262
|
) {
|
|
7127
|
-
|
|
7263
|
+
// Issue #558: In C++ mode, primitives that become references don't need dereferencing
|
|
7264
|
+
return this.cppMode ? id : `(*${id})`;
|
|
7128
7265
|
}
|
|
7129
7266
|
return id;
|
|
7130
7267
|
}
|
|
@@ -8657,7 +8794,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
8657
8794
|
!paramInfo.isStruct &&
|
|
8658
8795
|
this._isKnownPrimitive(paramInfo.baseType)
|
|
8659
8796
|
) {
|
|
8660
|
-
|
|
8797
|
+
// Issue #558: In C++ mode, primitives that become references don't need dereferencing
|
|
8798
|
+
return this.cppMode ? id : `(*${id})`;
|
|
8661
8799
|
}
|
|
8662
8800
|
return id;
|
|
8663
8801
|
}
|
package/src/pipeline/Pipeline.ts
CHANGED
|
@@ -82,6 +82,17 @@ class Pipeline {
|
|
|
82
82
|
* Used to include C headers in generated .h files instead of forward-declaring types.
|
|
83
83
|
*/
|
|
84
84
|
private readonly headerIncludeDirectives: Map<string, string> = new Map();
|
|
85
|
+
/**
|
|
86
|
+
* Issue #558: Accumulated parameter modifications across all processed files.
|
|
87
|
+
* Used for cross-file const inference in C++ mode.
|
|
88
|
+
*/
|
|
89
|
+
private readonly accumulatedModifications: Map<string, Set<string>> =
|
|
90
|
+
new Map();
|
|
91
|
+
/**
|
|
92
|
+
* Issue #558: Accumulated function parameter lists across all processed files.
|
|
93
|
+
* Used for cross-file const inference transitive propagation.
|
|
94
|
+
*/
|
|
95
|
+
private readonly accumulatedParamLists: Map<string, string[]> = new Map();
|
|
85
96
|
|
|
86
97
|
constructor(config: IPipelineConfig) {
|
|
87
98
|
// Apply defaults
|
|
@@ -137,6 +148,10 @@ class Pipeline {
|
|
|
137
148
|
await this.cacheManager.initialize();
|
|
138
149
|
}
|
|
139
150
|
|
|
151
|
+
// Issue #558: Reset cross-file modification tracking for new run
|
|
152
|
+
this.accumulatedModifications.clear();
|
|
153
|
+
this.accumulatedParamLists.clear();
|
|
154
|
+
|
|
140
155
|
// Stage 1: Discover source files
|
|
141
156
|
const { cnextFiles, headerFiles } = await this.discoverSources();
|
|
142
157
|
|
|
@@ -146,6 +161,15 @@ class Pipeline {
|
|
|
146
161
|
return result;
|
|
147
162
|
}
|
|
148
163
|
|
|
164
|
+
// Issue #558: Sort files by dependency order for correct cross-file const inference.
|
|
165
|
+
// Files that are included by others should be processed first so their
|
|
166
|
+
// parameter modifications are available during transitive propagation.
|
|
167
|
+
// Simple heuristic: reverse the discovery order since includes are added
|
|
168
|
+
// after the files that include them.
|
|
169
|
+
if (this.cppDetected) {
|
|
170
|
+
cnextFiles.reverse();
|
|
171
|
+
}
|
|
172
|
+
|
|
149
173
|
// Ensure output directory exists if specified
|
|
150
174
|
if (this.config.outDir && !existsSync(this.config.outDir)) {
|
|
151
175
|
mkdirSync(this.config.outDir, { recursive: true });
|
|
@@ -575,6 +599,7 @@ class Pipeline {
|
|
|
575
599
|
|
|
576
600
|
/**
|
|
577
601
|
* Stage 3: Collect symbols from a C-Next file
|
|
602
|
+
* Issue #561: Also collects modification analysis in C++ mode for unified cross-file const inference
|
|
578
603
|
*/
|
|
579
604
|
private collectCNextSymbols(file: IDiscoveredFile): void {
|
|
580
605
|
const content = readFileSync(file.path, "utf-8");
|
|
@@ -616,6 +641,32 @@ class Pipeline {
|
|
|
616
641
|
// This ensures enum member info is available for all files before code generation
|
|
617
642
|
const symbolInfo = TSymbolInfoAdapter.convert(tSymbols);
|
|
618
643
|
this.symbolInfoByFile.set(file.path, symbolInfo);
|
|
644
|
+
|
|
645
|
+
// Issue #561: Collect modification analysis in C++ mode for cross-file const inference
|
|
646
|
+
// This unifies behavior between run() and transpileSource() code paths
|
|
647
|
+
if (this.cppDetected) {
|
|
648
|
+
const { modifications, paramLists } =
|
|
649
|
+
this.codeGenerator.analyzeModificationsOnly(tree);
|
|
650
|
+
|
|
651
|
+
// Accumulate modifications
|
|
652
|
+
for (const [funcName, params] of modifications) {
|
|
653
|
+
const existing = this.accumulatedModifications.get(funcName);
|
|
654
|
+
if (existing) {
|
|
655
|
+
for (const param of params) {
|
|
656
|
+
existing.add(param);
|
|
657
|
+
}
|
|
658
|
+
} else {
|
|
659
|
+
this.accumulatedModifications.set(funcName, new Set(params));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Accumulate param lists
|
|
664
|
+
for (const [funcName, params] of paramLists) {
|
|
665
|
+
if (!this.accumulatedParamLists.has(funcName)) {
|
|
666
|
+
this.accumulatedParamLists.set(funcName, [...params]);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
619
670
|
}
|
|
620
671
|
|
|
621
672
|
/**
|
|
@@ -788,6 +839,14 @@ class Pipeline {
|
|
|
788
839
|
);
|
|
789
840
|
}
|
|
790
841
|
|
|
842
|
+
// Issue #558: Inject cross-file data for const inference
|
|
843
|
+
if (this.cppDetected && this.accumulatedModifications.size > 0) {
|
|
844
|
+
this.codeGenerator.setCrossFileModifications(
|
|
845
|
+
this.accumulatedModifications,
|
|
846
|
+
this.accumulatedParamLists,
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
791
850
|
const code = this.codeGenerator.generate(
|
|
792
851
|
tree,
|
|
793
852
|
this.symbolTable,
|
|
@@ -822,6 +881,27 @@ class Pipeline {
|
|
|
822
881
|
}
|
|
823
882
|
this.passByValueParamsCollectors.set(file.path, passByValueCopy);
|
|
824
883
|
|
|
884
|
+
// Issue #558: Collect modifications and param lists for cross-file const inference
|
|
885
|
+
if (this.cppDetected) {
|
|
886
|
+
const fileModifications = this.codeGenerator.getModifiedParameters();
|
|
887
|
+
for (const [funcName, params] of fileModifications) {
|
|
888
|
+
const existing = this.accumulatedModifications.get(funcName);
|
|
889
|
+
if (existing) {
|
|
890
|
+
for (const param of params) {
|
|
891
|
+
existing.add(param);
|
|
892
|
+
}
|
|
893
|
+
} else {
|
|
894
|
+
this.accumulatedModifications.set(funcName, new Set(params));
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
const fileParamLists = this.codeGenerator.getFunctionParamLists();
|
|
898
|
+
for (const [funcName, params] of fileParamLists) {
|
|
899
|
+
if (!this.accumulatedParamLists.has(funcName)) {
|
|
900
|
+
this.accumulatedParamLists.set(funcName, [...params]);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
825
905
|
// Issue #424: Store user includes for header generation
|
|
826
906
|
// These may define macros used in array dimensions
|
|
827
907
|
// Issue #461: Transform .cnx includes to .h for header generation
|
|
@@ -1188,6 +1268,11 @@ class Pipeline {
|
|
|
1188
1268
|
await this.cacheManager.initialize();
|
|
1189
1269
|
}
|
|
1190
1270
|
|
|
1271
|
+
// Issue #561: Clear cross-file modification tracking for fresh analysis
|
|
1272
|
+
// This ensures each transpileSource() call starts with a clean slate
|
|
1273
|
+
this.accumulatedModifications.clear();
|
|
1274
|
+
this.accumulatedParamLists.clear();
|
|
1275
|
+
|
|
1191
1276
|
// Step 1: Build search paths using unified IncludeResolver
|
|
1192
1277
|
const searchPaths = IncludeResolver.buildSearchPaths(
|
|
1193
1278
|
workingDir,
|
|
@@ -1362,6 +1447,15 @@ class Pipeline {
|
|
|
1362
1447
|
);
|
|
1363
1448
|
}
|
|
1364
1449
|
|
|
1450
|
+
// Issue #561: Inject cross-file modification data for const inference
|
|
1451
|
+
// This unifies behavior with run() - both paths now share modifications from includes
|
|
1452
|
+
if (this.cppDetected && this.accumulatedModifications.size > 0) {
|
|
1453
|
+
this.codeGenerator.setCrossFileModifications(
|
|
1454
|
+
this.accumulatedModifications,
|
|
1455
|
+
this.accumulatedParamLists,
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1365
1459
|
const code = this.codeGenerator.generate(
|
|
1366
1460
|
tree,
|
|
1367
1461
|
this.symbolTable,
|
package/src/project/Project.ts
CHANGED
|
@@ -63,6 +63,13 @@ class Project {
|
|
|
63
63
|
* Compile the project
|
|
64
64
|
*/
|
|
65
65
|
async compile(): Promise<IProjectResult> {
|
|
66
|
+
// Issue #558: For C++ mode, use Pipeline.run() to enable cross-file const inference.
|
|
67
|
+
// This is needed because struct parameters passed to modifying functions in other
|
|
68
|
+
// files must not be marked const.
|
|
69
|
+
if (this.config.cppRequired) {
|
|
70
|
+
return this.compileWithPipeline();
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
// Compile each input file individually to avoid cross‑file symbol leakage.
|
|
67
74
|
const results: any[] = [];
|
|
68
75
|
// Build the full list of .cnx/.cnext files from srcDirs and explicit files
|
|
@@ -175,6 +182,30 @@ class Project {
|
|
|
175
182
|
|
|
176
183
|
return aggregate;
|
|
177
184
|
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Issue #558: Compile using Pipeline.run() for cross-file const inference in C++ mode.
|
|
188
|
+
*/
|
|
189
|
+
private async compileWithPipeline(): Promise<IProjectResult> {
|
|
190
|
+
const pipelineResult = await this.pipeline.run();
|
|
191
|
+
|
|
192
|
+
// Convert Pipeline result to Project result format
|
|
193
|
+
return {
|
|
194
|
+
success: pipelineResult.success,
|
|
195
|
+
filesProcessed: pipelineResult.filesProcessed,
|
|
196
|
+
symbolsCollected: pipelineResult.symbolsCollected,
|
|
197
|
+
conflicts: pipelineResult.conflicts,
|
|
198
|
+
errors: pipelineResult.errors.map((e) => {
|
|
199
|
+
if (typeof e === "string") return e;
|
|
200
|
+
const path = (e as any).sourcePath ?? "";
|
|
201
|
+
return path
|
|
202
|
+
? `${path}:${e.line}:${e.column} ${e.message}`
|
|
203
|
+
: `${e.line}:${e.column} ${e.message}`;
|
|
204
|
+
}),
|
|
205
|
+
warnings: pipelineResult.warnings,
|
|
206
|
+
outputFiles: pipelineResult.outputFiles,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
178
209
|
/**
|
|
179
210
|
* Get the symbol table (for testing/inspection)
|
|
180
211
|
*/
|