c-next 0.2.3 → 0.2.4
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 +59 -57
- package/dist/index.js +537 -170
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/cli/Runner.ts +1 -1
- package/src/cli/__tests__/Runner.test.ts +8 -8
- package/src/cli/serve/ServeCommand.ts +29 -9
- package/src/transpiler/Transpiler.ts +105 -200
- package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
- package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
- package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
- package/src/transpiler/data/IncludeResolver.ts +11 -3
- package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
- package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +170 -14
- package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +327 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
- package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
- package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
- package/src/transpiler/logic/symbols/c/index.ts +50 -1
- package/src/transpiler/output/codegen/CodeGenerator.ts +31 -5
- package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
- package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +86 -23
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +1 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +9 -1
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
- package/src/transpiler/state/CodeGenState.ts +6 -0
- package/src/transpiler/types/IPipelineFile.ts +2 -2
- package/src/transpiler/types/IPipelineInput.ts +1 -1
- package/src/transpiler/types/TTranspileInput.ts +18 -0
- package/src/utils/constants/TypeConstants.ts +22 -0
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
10
10
|
// package.json
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "c-next",
|
|
13
|
-
version: "0.2.
|
|
13
|
+
version: "0.2.4",
|
|
14
14
|
description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
|
|
15
15
|
packageManager: "npm@11.9.0",
|
|
16
16
|
type: "module",
|
|
@@ -110764,6 +110764,8 @@ var CodeGenState = class {
|
|
|
110764
110764
|
static callbackTypes = /* @__PURE__ */ new Map();
|
|
110765
110765
|
/** Callback field types: "Struct.field" -> callbackTypeName */
|
|
110766
110766
|
static callbackFieldTypes = /* @__PURE__ */ new Map();
|
|
110767
|
+
/** Functions that need C-callback-compatible (by-value) struct parameters */
|
|
110768
|
+
static callbackCompatibleFunctions = /* @__PURE__ */ new Set();
|
|
110767
110769
|
// ===========================================================================
|
|
110768
110770
|
// PASS-BY-VALUE ANALYSIS (Issue #269)
|
|
110769
110771
|
// ===========================================================================
|
|
@@ -114074,14 +114076,14 @@ var TypeValidator = class _TypeValidator {
|
|
|
114074
114076
|
}
|
|
114075
114077
|
}
|
|
114076
114078
|
// ========================================================================
|
|
114077
|
-
//
|
|
114079
|
+
// Condition Boolean Validation (ADR-027, Issue #884)
|
|
114078
114080
|
// ========================================================================
|
|
114079
|
-
static
|
|
114081
|
+
static validateConditionIsBoolean(ctx, conditionType) {
|
|
114080
114082
|
const ternaryExpr = ctx.ternaryExpression();
|
|
114081
114083
|
const orExprs = ternaryExpr.orExpression();
|
|
114082
114084
|
if (orExprs.length !== 1) {
|
|
114083
114085
|
throw new Error(
|
|
114084
|
-
`Error E0701:
|
|
114086
|
+
`Error E0701: ${conditionType} condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`
|
|
114085
114087
|
);
|
|
114086
114088
|
}
|
|
114087
114089
|
const orExpr = orExprs[0];
|
|
@@ -114092,7 +114094,7 @@ var TypeValidator = class _TypeValidator {
|
|
|
114092
114094
|
const andExpr = orExpr.andExpression(0);
|
|
114093
114095
|
if (!andExpr) {
|
|
114094
114096
|
throw new Error(
|
|
114095
|
-
`Error E0701:
|
|
114097
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`
|
|
114096
114098
|
);
|
|
114097
114099
|
}
|
|
114098
114100
|
if (andExpr.equalityExpression().length > 1) {
|
|
@@ -114101,7 +114103,7 @@ var TypeValidator = class _TypeValidator {
|
|
|
114101
114103
|
const equalityExpr = andExpr.equalityExpression(0);
|
|
114102
114104
|
if (!equalityExpr) {
|
|
114103
114105
|
throw new Error(
|
|
114104
|
-
`Error E0701:
|
|
114106
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`
|
|
114105
114107
|
);
|
|
114106
114108
|
}
|
|
114107
114109
|
if (equalityExpr.relationalExpression().length > 1) {
|
|
@@ -114110,7 +114112,7 @@ var TypeValidator = class _TypeValidator {
|
|
|
114110
114112
|
const relationalExpr = equalityExpr.relationalExpression(0);
|
|
114111
114113
|
if (!relationalExpr) {
|
|
114112
114114
|
throw new Error(
|
|
114113
|
-
`Error E0701:
|
|
114115
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`
|
|
114114
114116
|
);
|
|
114115
114117
|
}
|
|
114116
114118
|
if (relationalExpr.bitwiseOrExpression().length > 1) {
|
|
@@ -114121,7 +114123,7 @@ var TypeValidator = class _TypeValidator {
|
|
|
114121
114123
|
return;
|
|
114122
114124
|
}
|
|
114123
114125
|
throw new Error(
|
|
114124
|
-
`Error E0701:
|
|
114126
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)
|
|
114125
114127
|
help: use explicit comparison: ${text} > 0 or ${text} != 0`
|
|
114126
114128
|
);
|
|
114127
114129
|
}
|
|
@@ -116940,6 +116942,7 @@ var generateIf = (node, _input, _state, orchestrator) => {
|
|
|
116940
116942
|
}
|
|
116941
116943
|
const cacheDecls = orchestrator.setupLengthCache(lengthCounts);
|
|
116942
116944
|
orchestrator.validateConditionNoFunctionCall(node.expression(), "if");
|
|
116945
|
+
orchestrator.validateConditionIsBoolean(node.expression(), "if");
|
|
116943
116946
|
const condition = orchestrator.generateExpression(node.expression());
|
|
116944
116947
|
const conditionTemps = orchestrator.flushPendingTempDeclarations();
|
|
116945
116948
|
const thenBranch = orchestrator.generateStatement(thenStmt);
|
|
@@ -116960,6 +116963,7 @@ var generateIf = (node, _input, _state, orchestrator) => {
|
|
|
116960
116963
|
var generateWhile = (node, _input, _state, orchestrator) => {
|
|
116961
116964
|
const effects = [];
|
|
116962
116965
|
orchestrator.validateConditionNoFunctionCall(node.expression(), "while");
|
|
116966
|
+
orchestrator.validateConditionIsBoolean(node.expression(), "while");
|
|
116963
116967
|
const condition = orchestrator.generateExpression(node.expression());
|
|
116964
116968
|
const conditionTemps = orchestrator.flushPendingTempDeclarations();
|
|
116965
116969
|
const body = orchestrator.generateStatement(node.statement());
|
|
@@ -116971,8 +116975,8 @@ var generateWhile = (node, _input, _state, orchestrator) => {
|
|
|
116971
116975
|
};
|
|
116972
116976
|
var generateDoWhile = (node, _input, _state, orchestrator) => {
|
|
116973
116977
|
const effects = [];
|
|
116974
|
-
orchestrator.validateDoWhileCondition(node.expression());
|
|
116975
116978
|
orchestrator.validateConditionNoFunctionCall(node.expression(), "do-while");
|
|
116979
|
+
orchestrator.validateConditionIsBoolean(node.expression(), "do-while");
|
|
116976
116980
|
const body = orchestrator.generateBlock(node.block());
|
|
116977
116981
|
const condition = orchestrator.generateExpression(node.expression());
|
|
116978
116982
|
const conditionTemps = orchestrator.flushPendingTempDeclarations();
|
|
@@ -117037,6 +117041,7 @@ var generateFor = (node, input, state, orchestrator) => {
|
|
|
117037
117041
|
let condition = "";
|
|
117038
117042
|
if (node.expression()) {
|
|
117039
117043
|
orchestrator.validateConditionNoFunctionCall(node.expression(), "for");
|
|
117044
|
+
orchestrator.validateConditionIsBoolean(node.expression(), "for");
|
|
117040
117045
|
condition = orchestrator.generateExpression(node.expression());
|
|
117041
117046
|
}
|
|
117042
117047
|
const conditionTemps = orchestrator.flushPendingTempDeclarations();
|
|
@@ -125526,11 +125531,14 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125526
125531
|
typeCtx,
|
|
125527
125532
|
callbacks
|
|
125528
125533
|
);
|
|
125534
|
+
const isCallbackCompat = CodeGenState.currentFunctionName !== null && CodeGenState.callbackCompatibleFunctions.has(
|
|
125535
|
+
CodeGenState.currentFunctionName
|
|
125536
|
+
);
|
|
125529
125537
|
const paramInfo = {
|
|
125530
125538
|
name,
|
|
125531
125539
|
baseType: typeInfo.typeName,
|
|
125532
125540
|
isArray,
|
|
125533
|
-
isStruct: typeInfo.isStruct,
|
|
125541
|
+
isStruct: isCallbackCompat ? false : typeInfo.isStruct,
|
|
125534
125542
|
isConst,
|
|
125535
125543
|
isCallback: typeInfo.isCallback,
|
|
125536
125544
|
isString: typeInfo.isString
|
|
@@ -127561,11 +127569,11 @@ var CodeGenerator = class _CodeGenerator {
|
|
|
127561
127569
|
TypeValidator_default.validateSwitchStatement(ctx, switchExpr);
|
|
127562
127570
|
}
|
|
127563
127571
|
/**
|
|
127564
|
-
* Validate
|
|
127572
|
+
* Validate condition is a boolean expression (ADR-027, Issue #884).
|
|
127565
127573
|
* Part of IOrchestrator interface (ADR-053 A3).
|
|
127566
127574
|
*/
|
|
127567
|
-
|
|
127568
|
-
TypeValidator_default.
|
|
127575
|
+
validateConditionIsBoolean(ctx, conditionType) {
|
|
127576
|
+
TypeValidator_default.validateConditionIsBoolean(ctx, conditionType);
|
|
127569
127577
|
}
|
|
127570
127578
|
/**
|
|
127571
127579
|
* Issue #254: Validate no function calls in condition (E0702).
|
|
@@ -127951,8 +127959,8 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
127951
127959
|
}
|
|
127952
127960
|
/**
|
|
127953
127961
|
* Issue #561: Analyze modifications in a parse tree without full code generation.
|
|
127954
|
-
* Used by
|
|
127955
|
-
* for cross-file const inference
|
|
127962
|
+
* Used by the transpile() pipeline to collect modification info from includes
|
|
127963
|
+
* for cross-file const inference.
|
|
127956
127964
|
*
|
|
127957
127965
|
* Issue #565: Now accepts optional cross-file data for transitive propagation.
|
|
127958
127966
|
* When a file calls a function from an included file that modifies its param,
|
|
@@ -129333,6 +129341,9 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129333
129341
|
return "{}";
|
|
129334
129342
|
}
|
|
129335
129343
|
const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
|
|
129344
|
+
if (CodeGenState.cppMode && (typeName.startsWith("struct {") || typeName.startsWith("union {"))) {
|
|
129345
|
+
return `{ ${fieldInits.join(", ")} }`;
|
|
129346
|
+
}
|
|
129336
129347
|
return `(${castType}){ ${fieldInits.join(", ")} }`;
|
|
129337
129348
|
}
|
|
129338
129349
|
/**
|
|
@@ -129516,6 +129527,11 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129516
129527
|
)) {
|
|
129517
129528
|
return true;
|
|
129518
129529
|
}
|
|
129530
|
+
if (CodeGenState.currentFunctionName && CodeGenState.callbackCompatibleFunctions.has(
|
|
129531
|
+
CodeGenState.currentFunctionName
|
|
129532
|
+
) && this.isKnownStruct(typeName)) {
|
|
129533
|
+
return true;
|
|
129534
|
+
}
|
|
129519
129535
|
return false;
|
|
129520
129536
|
}
|
|
129521
129537
|
// ========================================================================
|
|
@@ -130728,8 +130744,11 @@ var HeaderGeneratorUtils = class _HeaderGeneratorUtils {
|
|
|
130728
130744
|
lines.push(include);
|
|
130729
130745
|
}
|
|
130730
130746
|
}
|
|
130747
|
+
const userIncludeSet = new Set(options.userIncludes ?? []);
|
|
130731
130748
|
for (const directive of headersToInclude) {
|
|
130732
|
-
|
|
130749
|
+
if (!userIncludeSet.has(directive)) {
|
|
130750
|
+
lines.push(directive);
|
|
130751
|
+
}
|
|
130733
130752
|
}
|
|
130734
130753
|
const hasIncludes = options.includeSystemHeaders !== false || options.userIncludes && options.userIncludes.length > 0 || headersToInclude.size > 0;
|
|
130735
130754
|
if (hasIncludes) {
|
|
@@ -133342,8 +133361,9 @@ var CResolver = class _CResolver {
|
|
|
133342
133361
|
if (!name) continue;
|
|
133343
133362
|
const isFunction = DeclaratorUtils_default.declaratorIsFunction(declarator);
|
|
133344
133363
|
if (ctx.isTypedef) {
|
|
133364
|
+
const typedefType = _CResolver.isFunctionPointerDeclarator(declarator) ? `${baseType} (*)(${_CResolver.extractParamText(declarator)})` : baseType;
|
|
133345
133365
|
ctx.symbols.push(
|
|
133346
|
-
TypedefCollector_default.collect(name,
|
|
133366
|
+
TypedefCollector_default.collect(name, typedefType, ctx.sourceFile, ctx.line)
|
|
133347
133367
|
);
|
|
133348
133368
|
} else if (isFunction) {
|
|
133349
133369
|
ctx.symbols.push(
|
|
@@ -133446,6 +133466,36 @@ var CResolver = class _CResolver {
|
|
|
133446
133466
|
}
|
|
133447
133467
|
return typeParts.join(" ") || "int";
|
|
133448
133468
|
}
|
|
133469
|
+
/**
|
|
133470
|
+
* Check if a declarator represents a function pointer.
|
|
133471
|
+
* For `(*PointCallback)(Point p)`, the C grammar parses as:
|
|
133472
|
+
* declarator -> directDeclarator
|
|
133473
|
+
* directDeclarator -> directDeclarator '(' parameterTypeList ')'
|
|
133474
|
+
* inner directDeclarator -> '(' declarator ')'
|
|
133475
|
+
* inner declarator -> pointer directDeclarator -> * PointCallback
|
|
133476
|
+
*/
|
|
133477
|
+
static isFunctionPointerDeclarator(declarator) {
|
|
133478
|
+
const directDecl = declarator.directDeclarator?.();
|
|
133479
|
+
if (!directDecl) return false;
|
|
133480
|
+
const hasParams = directDecl.parameterTypeList?.() !== null || Boolean(directDecl.LeftParen?.());
|
|
133481
|
+
if (!hasParams) return false;
|
|
133482
|
+
const innerDirectDecl = directDecl.directDeclarator?.();
|
|
133483
|
+
if (!innerDirectDecl) return false;
|
|
133484
|
+
const nestedDecl = innerDirectDecl.declarator?.();
|
|
133485
|
+
if (!nestedDecl) return false;
|
|
133486
|
+
return Boolean(nestedDecl.pointer?.());
|
|
133487
|
+
}
|
|
133488
|
+
/**
|
|
133489
|
+
* Extract parameter text from a function pointer declarator.
|
|
133490
|
+
* Returns the text of the parameters from a function pointer like "(*Callback)(Point p)".
|
|
133491
|
+
*/
|
|
133492
|
+
static extractParamText(declarator) {
|
|
133493
|
+
const directDecl = declarator.directDeclarator?.();
|
|
133494
|
+
if (!directDecl) return "";
|
|
133495
|
+
const paramTypeList = directDecl.parameterTypeList?.();
|
|
133496
|
+
if (!paramTypeList) return "";
|
|
133497
|
+
return paramTypeList.getText();
|
|
133498
|
+
}
|
|
133449
133499
|
};
|
|
133450
133500
|
var c_default = CResolver;
|
|
133451
133501
|
|
|
@@ -135075,6 +135125,9 @@ var IncludeResolver = class _IncludeResolver {
|
|
|
135075
135125
|
}
|
|
135076
135126
|
if (file.type === EFileType_default.CNext) {
|
|
135077
135127
|
result.cnextIncludes.push(file);
|
|
135128
|
+
const headerPath = includeInfo.path.replace(/\.cnx$|\.cnext$/, ".h");
|
|
135129
|
+
const directive = includeInfo.isLocal ? `#include "${headerPath}"` : `#include <${headerPath}>`;
|
|
135130
|
+
result.headerIncludeDirectives.set(absolutePath, directive);
|
|
135078
135131
|
}
|
|
135079
135132
|
}
|
|
135080
135133
|
/**
|
|
@@ -135212,8 +135265,8 @@ var IncludeResolver = class _IncludeResolver {
|
|
|
135212
135265
|
/**
|
|
135213
135266
|
* Build search paths from a source file location
|
|
135214
135267
|
*
|
|
135215
|
-
* Consolidates the search path building logic used by
|
|
135216
|
-
*
|
|
135268
|
+
* Consolidates the search path building logic used by the unified
|
|
135269
|
+
* transpile() entry point.
|
|
135217
135270
|
*
|
|
135218
135271
|
* Search order (highest to lowest priority):
|
|
135219
135272
|
* 1. Source file's directory (for relative includes)
|
|
@@ -136548,16 +136601,20 @@ var FunctionCallListener = class extends CNextListener {
|
|
|
136548
136601
|
// ========================================================================
|
|
136549
136602
|
// ISR/Callback Variable Tracking (ADR-040)
|
|
136550
136603
|
// ========================================================================
|
|
136604
|
+
/**
|
|
136605
|
+
* Check if a type name represents a callable type (ISR, callback, or C function pointer typedef).
|
|
136606
|
+
*/
|
|
136607
|
+
isCallableType(typeName) {
|
|
136608
|
+
return typeName === "ISR" || this.analyzer.isCallbackType(typeName) || this.analyzer.isCFunctionPointerTypedef(typeName);
|
|
136609
|
+
}
|
|
136551
136610
|
/**
|
|
136552
136611
|
* Track ISR-typed variables from variable declarations
|
|
136553
136612
|
* e.g., `ISR handler <- myFunction;`
|
|
136554
136613
|
*/
|
|
136555
136614
|
enterVariableDeclaration = (ctx) => {
|
|
136556
|
-
const
|
|
136557
|
-
|
|
136558
|
-
|
|
136559
|
-
const varName = ctx.IDENTIFIER().getText();
|
|
136560
|
-
this.analyzer.defineCallableVariable(varName);
|
|
136615
|
+
const typeName = ctx.type().getText();
|
|
136616
|
+
if (this.isCallableType(typeName)) {
|
|
136617
|
+
this.analyzer.defineCallableVariable(ctx.IDENTIFIER().getText());
|
|
136561
136618
|
}
|
|
136562
136619
|
};
|
|
136563
136620
|
/**
|
|
@@ -136565,11 +136622,9 @@ var FunctionCallListener = class extends CNextListener {
|
|
|
136565
136622
|
* e.g., `void execute(ISR handler) { handler(); }`
|
|
136566
136623
|
*/
|
|
136567
136624
|
enterParameter = (ctx) => {
|
|
136568
|
-
const
|
|
136569
|
-
|
|
136570
|
-
|
|
136571
|
-
const paramName = ctx.IDENTIFIER().getText();
|
|
136572
|
-
this.analyzer.defineCallableVariable(paramName);
|
|
136625
|
+
const typeName = ctx.type().getText();
|
|
136626
|
+
if (this.isCallableType(typeName)) {
|
|
136627
|
+
this.analyzer.defineCallableVariable(ctx.IDENTIFIER().getText());
|
|
136573
136628
|
}
|
|
136574
136629
|
};
|
|
136575
136630
|
// ========================================================================
|
|
@@ -136708,6 +136763,7 @@ var FunctionCallAnalyzer = class {
|
|
|
136708
136763
|
this.collectIncludes(tree);
|
|
136709
136764
|
this.collectCallbackTypes(tree);
|
|
136710
136765
|
this.collectAllLocalFunctions(tree);
|
|
136766
|
+
this.collectCallbackCompatibleFunctions(tree);
|
|
136711
136767
|
const listener = new FunctionCallListener(this);
|
|
136712
136768
|
ParseTreeWalker5.DEFAULT.walk(listener, tree);
|
|
136713
136769
|
return this.errors;
|
|
@@ -136791,6 +136847,120 @@ var FunctionCallAnalyzer = class {
|
|
|
136791
136847
|
isCallbackType(name) {
|
|
136792
136848
|
return this.callbackTypes.has(name);
|
|
136793
136849
|
}
|
|
136850
|
+
/**
|
|
136851
|
+
* Check if a type name is a C function pointer typedef.
|
|
136852
|
+
* Looks up the type in the symbol table and checks if it's a typedef
|
|
136853
|
+
* whose underlying type contains "(*)" indicating a function pointer.
|
|
136854
|
+
*/
|
|
136855
|
+
isCFunctionPointerTypedef(typeName) {
|
|
136856
|
+
if (!this.symbolTable) return false;
|
|
136857
|
+
const sym = this.symbolTable.getCSymbol(typeName);
|
|
136858
|
+
if (sym?.kind !== "type") return false;
|
|
136859
|
+
return "type" in sym && typeof sym.type === "string" && sym.type.includes("(*)");
|
|
136860
|
+
}
|
|
136861
|
+
/**
|
|
136862
|
+
* Detect functions assigned to C function pointer typedefs.
|
|
136863
|
+
* When `PointCallback cb <- my_handler;` is found and PointCallback
|
|
136864
|
+
* is a C function pointer typedef, mark my_handler as callback-compatible.
|
|
136865
|
+
*/
|
|
136866
|
+
collectCallbackCompatibleFunctions(tree) {
|
|
136867
|
+
for (const decl of tree.declaration()) {
|
|
136868
|
+
const funcDecl = decl.functionDeclaration();
|
|
136869
|
+
if (!funcDecl) continue;
|
|
136870
|
+
const block = funcDecl.block();
|
|
136871
|
+
if (!block) continue;
|
|
136872
|
+
this.scanBlockForCallbackAssignments(block);
|
|
136873
|
+
}
|
|
136874
|
+
}
|
|
136875
|
+
/**
|
|
136876
|
+
* Recursively scan all statements in a block for callback typedef assignments.
|
|
136877
|
+
*/
|
|
136878
|
+
scanBlockForCallbackAssignments(block) {
|
|
136879
|
+
for (const stmt of block.statement()) {
|
|
136880
|
+
this.scanStatementForCallbackAssignments(stmt);
|
|
136881
|
+
}
|
|
136882
|
+
}
|
|
136883
|
+
/**
|
|
136884
|
+
* Scan a single statement for callback typedef assignments,
|
|
136885
|
+
* recursing into nested blocks (if/while/for/do-while/switch/critical).
|
|
136886
|
+
*/
|
|
136887
|
+
scanStatementForCallbackAssignments(stmt) {
|
|
136888
|
+
const varDecl = stmt.variableDeclaration();
|
|
136889
|
+
if (varDecl) {
|
|
136890
|
+
this.checkVarDeclForCallbackAssignment(varDecl);
|
|
136891
|
+
return;
|
|
136892
|
+
}
|
|
136893
|
+
const ifStmt = stmt.ifStatement();
|
|
136894
|
+
if (ifStmt) {
|
|
136895
|
+
for (const child of ifStmt.statement()) {
|
|
136896
|
+
this.scanStatementForCallbackAssignments(child);
|
|
136897
|
+
}
|
|
136898
|
+
return;
|
|
136899
|
+
}
|
|
136900
|
+
const whileStmt = stmt.whileStatement();
|
|
136901
|
+
if (whileStmt) {
|
|
136902
|
+
this.scanStatementForCallbackAssignments(whileStmt.statement());
|
|
136903
|
+
return;
|
|
136904
|
+
}
|
|
136905
|
+
const forStmt = stmt.forStatement();
|
|
136906
|
+
if (forStmt) {
|
|
136907
|
+
this.scanStatementForCallbackAssignments(forStmt.statement());
|
|
136908
|
+
return;
|
|
136909
|
+
}
|
|
136910
|
+
const doWhileStmt = stmt.doWhileStatement();
|
|
136911
|
+
if (doWhileStmt) {
|
|
136912
|
+
this.scanBlockForCallbackAssignments(doWhileStmt.block());
|
|
136913
|
+
return;
|
|
136914
|
+
}
|
|
136915
|
+
const switchStmt = stmt.switchStatement();
|
|
136916
|
+
if (switchStmt) {
|
|
136917
|
+
for (const caseCtx of switchStmt.switchCase()) {
|
|
136918
|
+
this.scanBlockForCallbackAssignments(caseCtx.block());
|
|
136919
|
+
}
|
|
136920
|
+
const defaultCtx = switchStmt.defaultCase();
|
|
136921
|
+
if (defaultCtx) {
|
|
136922
|
+
this.scanBlockForCallbackAssignments(defaultCtx.block());
|
|
136923
|
+
}
|
|
136924
|
+
return;
|
|
136925
|
+
}
|
|
136926
|
+
const criticalStmt = stmt.criticalStatement();
|
|
136927
|
+
if (criticalStmt) {
|
|
136928
|
+
this.scanBlockForCallbackAssignments(criticalStmt.block());
|
|
136929
|
+
return;
|
|
136930
|
+
}
|
|
136931
|
+
const nestedBlock = stmt.block();
|
|
136932
|
+
if (nestedBlock) {
|
|
136933
|
+
this.scanBlockForCallbackAssignments(nestedBlock);
|
|
136934
|
+
}
|
|
136935
|
+
}
|
|
136936
|
+
/**
|
|
136937
|
+
* Check if a variable declaration assigns a function to a C callback typedef.
|
|
136938
|
+
*/
|
|
136939
|
+
checkVarDeclForCallbackAssignment(varDecl) {
|
|
136940
|
+
const typeName = varDecl.type().getText();
|
|
136941
|
+
if (!this.isCFunctionPointerTypedef(typeName)) return;
|
|
136942
|
+
const expr = varDecl.expression();
|
|
136943
|
+
if (!expr) return;
|
|
136944
|
+
const funcRef = this.extractFunctionReference(expr);
|
|
136945
|
+
if (!funcRef) return;
|
|
136946
|
+
const lookupName = funcRef.includes(".") ? funcRef.replace(".", "_") : funcRef;
|
|
136947
|
+
if (this.allLocalFunctions.has(lookupName)) {
|
|
136948
|
+
CodeGenState.callbackCompatibleFunctions.add(lookupName);
|
|
136949
|
+
}
|
|
136950
|
+
}
|
|
136951
|
+
/**
|
|
136952
|
+
* Extract a function reference from an expression context.
|
|
136953
|
+
* Matches bare identifiers (e.g., "my_handler") and qualified scope
|
|
136954
|
+
* names (e.g., "MyScope.handler").
|
|
136955
|
+
* Returns null if the expression is not a function reference.
|
|
136956
|
+
*/
|
|
136957
|
+
extractFunctionReference(expr) {
|
|
136958
|
+
const text = expr.getText();
|
|
136959
|
+
if (/^\w+(\.\w+)?$/.test(text)) {
|
|
136960
|
+
return text;
|
|
136961
|
+
}
|
|
136962
|
+
return null;
|
|
136963
|
+
}
|
|
136794
136964
|
/**
|
|
136795
136965
|
* ADR-040: Register a variable that holds a callable (ISR or callback)
|
|
136796
136966
|
*/
|
|
@@ -137044,6 +137214,26 @@ var TypeConstants = class {
|
|
|
137044
137214
|
"float",
|
|
137045
137215
|
"double"
|
|
137046
137216
|
];
|
|
137217
|
+
/**
|
|
137218
|
+
* Unsigned integer types valid as array/bit subscript indexes.
|
|
137219
|
+
*
|
|
137220
|
+
* Used by:
|
|
137221
|
+
* - ArrayIndexTypeAnalyzer: validating subscript index types
|
|
137222
|
+
*/
|
|
137223
|
+
static UNSIGNED_INDEX_TYPES = [
|
|
137224
|
+
"u8",
|
|
137225
|
+
"u16",
|
|
137226
|
+
"u32",
|
|
137227
|
+
"u64",
|
|
137228
|
+
"bool"
|
|
137229
|
+
];
|
|
137230
|
+
/**
|
|
137231
|
+
* Signed integer types (rejected as subscript indexes).
|
|
137232
|
+
*
|
|
137233
|
+
* Used by:
|
|
137234
|
+
* - ArrayIndexTypeAnalyzer: detecting signed integer subscript indexes
|
|
137235
|
+
*/
|
|
137236
|
+
static SIGNED_TYPES = ["i8", "i16", "i32", "i64"];
|
|
137047
137237
|
};
|
|
137048
137238
|
var TypeConstants_default = TypeConstants;
|
|
137049
137239
|
|
|
@@ -137161,6 +137351,212 @@ var FloatModuloAnalyzer = class {
|
|
|
137161
137351
|
};
|
|
137162
137352
|
var FloatModuloAnalyzer_default = FloatModuloAnalyzer;
|
|
137163
137353
|
|
|
137354
|
+
// src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts
|
|
137355
|
+
import { ParseTreeWalker as ParseTreeWalker8 } from "antlr4ng";
|
|
137356
|
+
var VariableTypeCollector = class extends CNextListener {
|
|
137357
|
+
varTypes = /* @__PURE__ */ new Map();
|
|
137358
|
+
getVarTypes() {
|
|
137359
|
+
return this.varTypes;
|
|
137360
|
+
}
|
|
137361
|
+
trackType(typeCtx, identifier) {
|
|
137362
|
+
if (!typeCtx || !identifier) return;
|
|
137363
|
+
this.varTypes.set(identifier.getText(), typeCtx.getText());
|
|
137364
|
+
}
|
|
137365
|
+
enterVariableDeclaration = (ctx) => {
|
|
137366
|
+
this.trackType(ctx.type(), ctx.IDENTIFIER());
|
|
137367
|
+
};
|
|
137368
|
+
enterParameter = (ctx) => {
|
|
137369
|
+
this.trackType(ctx.type(), ctx.IDENTIFIER());
|
|
137370
|
+
};
|
|
137371
|
+
enterForVarDecl = (ctx) => {
|
|
137372
|
+
this.trackType(ctx.type(), ctx.IDENTIFIER());
|
|
137373
|
+
};
|
|
137374
|
+
};
|
|
137375
|
+
var IndexTypeListener = class extends CNextListener {
|
|
137376
|
+
analyzer;
|
|
137377
|
+
// eslint-disable-next-line @typescript-eslint/lines-between-class-members
|
|
137378
|
+
varTypes;
|
|
137379
|
+
constructor(analyzer, varTypes) {
|
|
137380
|
+
super();
|
|
137381
|
+
this.analyzer = analyzer;
|
|
137382
|
+
this.varTypes = varTypes;
|
|
137383
|
+
}
|
|
137384
|
+
/**
|
|
137385
|
+
* Check postfix operations in expressions (RHS: arr[idx], flags[bit])
|
|
137386
|
+
*/
|
|
137387
|
+
enterPostfixOp = (ctx) => {
|
|
137388
|
+
if (!ctx.LBRACKET()) return;
|
|
137389
|
+
const expressions = ctx.expression();
|
|
137390
|
+
for (const expr of expressions) {
|
|
137391
|
+
this.validateIndexExpression(expr);
|
|
137392
|
+
}
|
|
137393
|
+
};
|
|
137394
|
+
/**
|
|
137395
|
+
* Check postfix target operations in assignments (LHS: arr[idx] <- val)
|
|
137396
|
+
*/
|
|
137397
|
+
enterPostfixTargetOp = (ctx) => {
|
|
137398
|
+
if (!ctx.LBRACKET()) return;
|
|
137399
|
+
const expressions = ctx.expression();
|
|
137400
|
+
for (const expr of expressions) {
|
|
137401
|
+
this.validateIndexExpression(expr);
|
|
137402
|
+
}
|
|
137403
|
+
};
|
|
137404
|
+
/**
|
|
137405
|
+
* Validate that a subscript index expression uses an unsigned integer type.
|
|
137406
|
+
* Collects all leaf operands from the expression and checks each one.
|
|
137407
|
+
*/
|
|
137408
|
+
validateIndexExpression(ctx) {
|
|
137409
|
+
const operands = this.collectOperands(ctx);
|
|
137410
|
+
for (const operand of operands) {
|
|
137411
|
+
const resolvedType = this.resolveOperandType(operand);
|
|
137412
|
+
if (!resolvedType) continue;
|
|
137413
|
+
if (TypeConstants_default.SIGNED_TYPES.includes(resolvedType)) {
|
|
137414
|
+
const { line: line2, column: column2 } = ParserUtils_default.getPosition(ctx);
|
|
137415
|
+
this.analyzer.addError(line2, column2, "E0850", resolvedType);
|
|
137416
|
+
return;
|
|
137417
|
+
}
|
|
137418
|
+
if (resolvedType === "float literal" || TypeConstants_default.FLOAT_TYPES.includes(resolvedType)) {
|
|
137419
|
+
const { line: line2, column: column2 } = ParserUtils_default.getPosition(ctx);
|
|
137420
|
+
this.analyzer.addError(line2, column2, "E0851", resolvedType);
|
|
137421
|
+
return;
|
|
137422
|
+
}
|
|
137423
|
+
if (TypeConstants_default.UNSIGNED_INDEX_TYPES.includes(resolvedType)) {
|
|
137424
|
+
continue;
|
|
137425
|
+
}
|
|
137426
|
+
const { line, column } = ParserUtils_default.getPosition(ctx);
|
|
137427
|
+
this.analyzer.addError(line, column, "E0852", resolvedType);
|
|
137428
|
+
return;
|
|
137429
|
+
}
|
|
137430
|
+
}
|
|
137431
|
+
/**
|
|
137432
|
+
* Collect all leaf unary expression operands from an expression tree.
|
|
137433
|
+
* Handles binary operators at any level by flatMapping through the grammar hierarchy.
|
|
137434
|
+
*/
|
|
137435
|
+
collectOperands(ctx) {
|
|
137436
|
+
const ternary = ctx.ternaryExpression();
|
|
137437
|
+
if (!ternary) return [];
|
|
137438
|
+
const orExpressions = ternary.orExpression();
|
|
137439
|
+
if (orExpressions.length === 0) return [];
|
|
137440
|
+
const valueExpressions = orExpressions.length === 3 ? orExpressions.slice(1) : orExpressions;
|
|
137441
|
+
return valueExpressions.flatMap((o) => o.andExpression()).flatMap((a) => a.equalityExpression()).flatMap((e) => e.relationalExpression()).flatMap((r) => r.bitwiseOrExpression()).flatMap((bo) => bo.bitwiseXorExpression()).flatMap((bx) => bx.bitwiseAndExpression()).flatMap((ba) => ba.shiftExpression()).flatMap((s) => s.additiveExpression()).flatMap((a) => a.multiplicativeExpression()).flatMap((m) => m.unaryExpression());
|
|
137442
|
+
}
|
|
137443
|
+
/**
|
|
137444
|
+
* Resolve the type of a unary expression operand.
|
|
137445
|
+
* Uses local varTypes first, then falls back to CodeGenState for
|
|
137446
|
+
* struct fields, function return types, and enum detection.
|
|
137447
|
+
*
|
|
137448
|
+
* Returns null if the type cannot be resolved (pass-through).
|
|
137449
|
+
*/
|
|
137450
|
+
resolveOperandType(operand) {
|
|
137451
|
+
const postfixExpr = operand.postfixExpression();
|
|
137452
|
+
if (!postfixExpr) return null;
|
|
137453
|
+
const primaryExpr = postfixExpr.primaryExpression();
|
|
137454
|
+
if (!primaryExpr) return null;
|
|
137455
|
+
let currentType = this.resolveBaseType(primaryExpr);
|
|
137456
|
+
const postfixOps = postfixExpr.postfixOp();
|
|
137457
|
+
if (!currentType && postfixOps.length > 0) {
|
|
137458
|
+
const identifier = primaryExpr.IDENTIFIER();
|
|
137459
|
+
if (identifier) {
|
|
137460
|
+
currentType = identifier.getText();
|
|
137461
|
+
}
|
|
137462
|
+
}
|
|
137463
|
+
for (const op of postfixOps) {
|
|
137464
|
+
if (!currentType) return null;
|
|
137465
|
+
currentType = this.resolvePostfixOpType(currentType, op);
|
|
137466
|
+
}
|
|
137467
|
+
return currentType;
|
|
137468
|
+
}
|
|
137469
|
+
/**
|
|
137470
|
+
* Resolve the base type of a primary expression.
|
|
137471
|
+
*/
|
|
137472
|
+
resolveBaseType(primaryExpr) {
|
|
137473
|
+
const literal = primaryExpr.literal();
|
|
137474
|
+
if (literal) {
|
|
137475
|
+
if (LiteralUtils_default.isFloat(literal)) return "float literal";
|
|
137476
|
+
return null;
|
|
137477
|
+
}
|
|
137478
|
+
const parenExpr = primaryExpr.expression();
|
|
137479
|
+
if (parenExpr) {
|
|
137480
|
+
const innerOperands = this.collectOperands(parenExpr);
|
|
137481
|
+
for (const innerOp of innerOperands) {
|
|
137482
|
+
const innerType = this.resolveOperandType(innerOp);
|
|
137483
|
+
if (innerType) return innerType;
|
|
137484
|
+
}
|
|
137485
|
+
return null;
|
|
137486
|
+
}
|
|
137487
|
+
const identifier = primaryExpr.IDENTIFIER();
|
|
137488
|
+
if (!identifier) return null;
|
|
137489
|
+
const varName = identifier.getText();
|
|
137490
|
+
const localType = this.varTypes.get(varName);
|
|
137491
|
+
if (localType) return localType;
|
|
137492
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(varName);
|
|
137493
|
+
if (typeInfo) return typeInfo.baseType;
|
|
137494
|
+
return null;
|
|
137495
|
+
}
|
|
137496
|
+
/**
|
|
137497
|
+
* Resolve the resulting type after applying a postfix operator.
|
|
137498
|
+
*/
|
|
137499
|
+
resolvePostfixOpType(currentType, op) {
|
|
137500
|
+
if (op.DOT()) {
|
|
137501
|
+
const fieldId = op.IDENTIFIER();
|
|
137502
|
+
if (!fieldId) return null;
|
|
137503
|
+
const fieldName = fieldId.getText();
|
|
137504
|
+
if (CodeGenState.isKnownEnum(currentType)) return null;
|
|
137505
|
+
const fieldType = CodeGenState.getStructFieldType(currentType, fieldName);
|
|
137506
|
+
return fieldType ?? null;
|
|
137507
|
+
}
|
|
137508
|
+
if (op.LBRACKET()) {
|
|
137509
|
+
if (TypeConstants_default.UNSIGNED_INDEX_TYPES.includes(currentType)) {
|
|
137510
|
+
return "bool";
|
|
137511
|
+
}
|
|
137512
|
+
if (TypeConstants_default.SIGNED_TYPES.includes(currentType)) {
|
|
137513
|
+
return "bool";
|
|
137514
|
+
}
|
|
137515
|
+
return currentType;
|
|
137516
|
+
}
|
|
137517
|
+
if (op.LPAREN()) {
|
|
137518
|
+
const returnType = CodeGenState.getFunctionReturnType(currentType);
|
|
137519
|
+
return returnType ?? null;
|
|
137520
|
+
}
|
|
137521
|
+
return null;
|
|
137522
|
+
}
|
|
137523
|
+
};
|
|
137524
|
+
var ArrayIndexTypeAnalyzer = class {
|
|
137525
|
+
errors = [];
|
|
137526
|
+
/**
|
|
137527
|
+
* Analyze the parse tree for invalid subscript index types
|
|
137528
|
+
*/
|
|
137529
|
+
analyze(tree) {
|
|
137530
|
+
this.errors = [];
|
|
137531
|
+
const collector = new VariableTypeCollector();
|
|
137532
|
+
ParseTreeWalker8.DEFAULT.walk(collector, tree);
|
|
137533
|
+
const varTypes = collector.getVarTypes();
|
|
137534
|
+
const listener = new IndexTypeListener(this, varTypes);
|
|
137535
|
+
ParseTreeWalker8.DEFAULT.walk(listener, tree);
|
|
137536
|
+
return this.errors;
|
|
137537
|
+
}
|
|
137538
|
+
/**
|
|
137539
|
+
* Add an index type error
|
|
137540
|
+
*/
|
|
137541
|
+
addError(line, column, code, actualType) {
|
|
137542
|
+
this.errors.push({
|
|
137543
|
+
code,
|
|
137544
|
+
line,
|
|
137545
|
+
column,
|
|
137546
|
+
actualType,
|
|
137547
|
+
message: `Subscript index must be an unsigned integer type; got '${actualType}'`,
|
|
137548
|
+
helpText: "Use an unsigned integer type (u8, u16, u32, u64) for array and bit subscript indexes."
|
|
137549
|
+
});
|
|
137550
|
+
}
|
|
137551
|
+
/**
|
|
137552
|
+
* Get all detected errors
|
|
137553
|
+
*/
|
|
137554
|
+
getErrors() {
|
|
137555
|
+
return this.errors;
|
|
137556
|
+
}
|
|
137557
|
+
};
|
|
137558
|
+
var ArrayIndexTypeAnalyzer_default = ArrayIndexTypeAnalyzer;
|
|
137559
|
+
|
|
137164
137560
|
// src/transpiler/logic/analysis/runAnalyzers.ts
|
|
137165
137561
|
function collectErrors(analyzerErrors, target, formatMessage) {
|
|
137166
137562
|
const formatter = formatMessage ?? ((e) => e.message);
|
|
@@ -137214,6 +137610,10 @@ function runAnalyzers(tree, tokenStream, options) {
|
|
|
137214
137610
|
if (collectErrors(floatModAnalyzer.analyze(tree), errors, formatWithCode)) {
|
|
137215
137611
|
return errors;
|
|
137216
137612
|
}
|
|
137613
|
+
const indexTypeAnalyzer = new ArrayIndexTypeAnalyzer_default();
|
|
137614
|
+
if (collectErrors(indexTypeAnalyzer.analyze(tree), errors, formatWithCode)) {
|
|
137615
|
+
return errors;
|
|
137616
|
+
}
|
|
137217
137617
|
const commentExtractor = new CommentExtractor_default(tokenStream);
|
|
137218
137618
|
collectErrors(
|
|
137219
137619
|
commentExtractor.validate(),
|
|
@@ -137821,7 +138221,7 @@ var TransitiveEnumCollector = class {
|
|
|
137821
138221
|
/**
|
|
137822
138222
|
* Collect symbol info for standalone mode from resolved includes.
|
|
137823
138223
|
*
|
|
137824
|
-
* Issue #591: Extracted
|
|
138224
|
+
* Issue #591: Extracted to unify enum collection across transpilation modes.
|
|
137825
138225
|
* Unlike collect() which starts from a file path and parses it, this method
|
|
137826
138226
|
* starts from already-resolved includes (from IncludeResolver.resolve()).
|
|
137827
138227
|
*
|
|
@@ -137899,83 +138299,53 @@ var Transpiler = class {
|
|
|
137899
138299
|
this.cacheManager = projectRoot ? new CacheManager_default(projectRoot, this.fs) : null;
|
|
137900
138300
|
}
|
|
137901
138301
|
// ===========================================================================
|
|
137902
|
-
// Public API
|
|
138302
|
+
// Public API
|
|
137903
138303
|
// ===========================================================================
|
|
137904
138304
|
/**
|
|
137905
|
-
*
|
|
138305
|
+
* Unified entry point for all transpilation.
|
|
137906
138306
|
*
|
|
137907
|
-
*
|
|
138307
|
+
* @param input - What to transpile:
|
|
138308
|
+
* - { kind: 'files' } — discover from config.inputs, write to disk
|
|
138309
|
+
* - { kind: 'source', source, ... } — transpile in-memory source
|
|
138310
|
+
* @returns ITranspilerResult with per-file results in .files[]
|
|
137908
138311
|
*/
|
|
137909
|
-
async
|
|
138312
|
+
async transpile(input) {
|
|
137910
138313
|
const result = this._initResult();
|
|
137911
138314
|
try {
|
|
137912
138315
|
await this._initializeRun();
|
|
137913
|
-
const
|
|
137914
|
-
if (cnextFiles.length === 0) {
|
|
138316
|
+
const pipelineInput = await this.discoverIncludes(input);
|
|
138317
|
+
if (pipelineInput.cnextFiles.length === 0) {
|
|
137915
138318
|
return this._finalizeResult(result, "No C-Next source files found");
|
|
137916
138319
|
}
|
|
137917
|
-
|
|
137918
|
-
|
|
137919
|
-
|
|
137920
|
-
|
|
137921
|
-
}));
|
|
137922
|
-
const input = {
|
|
137923
|
-
cnextFiles: pipelineFiles,
|
|
137924
|
-
headerFiles,
|
|
137925
|
-
writeOutputToDisk: true
|
|
137926
|
-
};
|
|
137927
|
-
await this._executePipeline(input, result);
|
|
138320
|
+
if (input.kind === "files") {
|
|
138321
|
+
this._ensureOutputDirectories();
|
|
138322
|
+
}
|
|
138323
|
+
await this._executePipeline(pipelineInput, result);
|
|
137928
138324
|
return await this._finalizeResult(result);
|
|
137929
138325
|
} catch (err) {
|
|
137930
138326
|
return this._handleRunError(result, err);
|
|
137931
138327
|
}
|
|
137932
138328
|
}
|
|
137933
138329
|
/**
|
|
137934
|
-
*
|
|
138330
|
+
* Stage 1: Discover files and build pipeline input.
|
|
137935
138331
|
*
|
|
137936
|
-
*
|
|
137937
|
-
*
|
|
138332
|
+
* Branches on input kind:
|
|
138333
|
+
* - 'files': filesystem scan, dependency graph, topological sort
|
|
138334
|
+
* - 'source': parse in-memory string, walk include tree
|
|
137938
138335
|
*
|
|
137939
|
-
*
|
|
137940
|
-
*
|
|
137941
|
-
|
|
137942
|
-
|
|
137943
|
-
|
|
137944
|
-
|
|
137945
|
-
|
|
137946
|
-
|
|
137947
|
-
|
|
137948
|
-
|
|
137949
|
-
|
|
137950
|
-
|
|
137951
|
-
|
|
137952
|
-
additionalIncludeDirs,
|
|
137953
|
-
sourcePath
|
|
137954
|
-
);
|
|
137955
|
-
const result = this._initResult();
|
|
137956
|
-
await this._executePipeline(input, result);
|
|
137957
|
-
const fileResult = result.files.find((f) => f.sourcePath === sourcePath);
|
|
137958
|
-
if (fileResult) {
|
|
137959
|
-
return fileResult;
|
|
137960
|
-
}
|
|
137961
|
-
if (result.errors.length > 0) {
|
|
137962
|
-
return this.buildErrorResult(sourcePath, result.errors, 0);
|
|
137963
|
-
}
|
|
137964
|
-
return this.buildErrorResult(
|
|
137965
|
-
sourcePath,
|
|
137966
|
-
[
|
|
137967
|
-
{
|
|
137968
|
-
line: 1,
|
|
137969
|
-
column: 0,
|
|
137970
|
-
message: "Pipeline produced no result for source file",
|
|
137971
|
-
severity: "error"
|
|
137972
|
-
}
|
|
137973
|
-
],
|
|
137974
|
-
0
|
|
137975
|
-
);
|
|
137976
|
-
} catch (err) {
|
|
137977
|
-
return this.buildCatchResult(sourcePath, err);
|
|
137978
|
-
}
|
|
138336
|
+
* Header directive storage happens via IncludeResolver.resolve() for both
|
|
138337
|
+
* C headers and cnext includes (Issue #854).
|
|
138338
|
+
*/
|
|
138339
|
+
async discoverIncludes(input) {
|
|
138340
|
+
if (input.kind === "files") {
|
|
138341
|
+
return this._discoverFromFiles();
|
|
138342
|
+
}
|
|
138343
|
+
return this._discoverFromSource(
|
|
138344
|
+
input.source,
|
|
138345
|
+
input.workingDir ?? process.cwd(),
|
|
138346
|
+
input.includeDirs ?? [],
|
|
138347
|
+
input.sourcePath ?? "<string>"
|
|
138348
|
+
);
|
|
137979
138349
|
}
|
|
137980
138350
|
// ===========================================================================
|
|
137981
138351
|
// Unified Pipeline
|
|
@@ -137983,7 +138353,7 @@ var Transpiler = class {
|
|
|
137983
138353
|
/**
|
|
137984
138354
|
* The single unified pipeline for all transpilation.
|
|
137985
138355
|
*
|
|
137986
|
-
*
|
|
138356
|
+
* transpile() delegates here after file discovery via discoverIncludes().
|
|
137987
138357
|
*
|
|
137988
138358
|
* Stage 2: Collect symbols from C/C++ headers (includes building analyzer context)
|
|
137989
138359
|
* Stage 3: Collect symbols from C-Next files
|
|
@@ -138090,14 +138460,6 @@ var Transpiler = class {
|
|
|
138090
138460
|
if (this.config.parseOnly) {
|
|
138091
138461
|
return this.buildParseOnlyResult(sourcePath, declarationCount);
|
|
138092
138462
|
}
|
|
138093
|
-
const analyzerErrors = runAnalyzers_default(tree, tokenStream);
|
|
138094
|
-
if (analyzerErrors.length > 0) {
|
|
138095
|
-
return this.buildErrorResult(
|
|
138096
|
-
sourcePath,
|
|
138097
|
-
analyzerErrors,
|
|
138098
|
-
declarationCount
|
|
138099
|
-
);
|
|
138100
|
-
}
|
|
138101
138463
|
const tSymbols = cnext_default.resolve(tree, sourcePath);
|
|
138102
138464
|
let symbolInfo = TSymbolInfoAdapter_default.convert(tSymbols);
|
|
138103
138465
|
const externalEnumSources = this._collectExternalEnumSources(
|
|
@@ -138110,6 +138472,15 @@ var Transpiler = class {
|
|
|
138110
138472
|
externalEnumSources
|
|
138111
138473
|
);
|
|
138112
138474
|
}
|
|
138475
|
+
CodeGenState.symbols = symbolInfo;
|
|
138476
|
+
const analyzerErrors = runAnalyzers_default(tree, tokenStream);
|
|
138477
|
+
if (analyzerErrors.length > 0) {
|
|
138478
|
+
return this.buildErrorResult(
|
|
138479
|
+
sourcePath,
|
|
138480
|
+
analyzerErrors,
|
|
138481
|
+
declarationCount
|
|
138482
|
+
);
|
|
138483
|
+
}
|
|
138113
138484
|
this._setupCrossFileModifications();
|
|
138114
138485
|
const code = this.codeGenerator.generate(tree, tokenStream, {
|
|
138115
138486
|
debugMode: this.config.debugMode,
|
|
@@ -138127,15 +138498,7 @@ var Transpiler = class {
|
|
|
138127
138498
|
if (this.cppDetected) {
|
|
138128
138499
|
this._accumulateFileModifications();
|
|
138129
138500
|
}
|
|
138130
|
-
const
|
|
138131
|
-
const headerCode = this.generateHeaderContent(
|
|
138132
|
-
fileSymbols,
|
|
138133
|
-
sourcePath,
|
|
138134
|
-
this.cppDetected,
|
|
138135
|
-
userIncludes,
|
|
138136
|
-
passByValueCopy,
|
|
138137
|
-
symbolInfo
|
|
138138
|
-
);
|
|
138501
|
+
const headerCode = this.generateHeaderForFile(file) ?? void 0;
|
|
138139
138502
|
return this.buildSuccessResult(
|
|
138140
138503
|
sourcePath,
|
|
138141
138504
|
code,
|
|
@@ -138200,6 +138563,13 @@ var Transpiler = class {
|
|
|
138200
138563
|
this.state.setHeaderDirective(header.path, directive);
|
|
138201
138564
|
}
|
|
138202
138565
|
}
|
|
138566
|
+
for (const cnxInclude of resolved.cnextIncludes) {
|
|
138567
|
+
const includePath = resolve12(cnxInclude.path);
|
|
138568
|
+
const directive = resolved.headerIncludeDirectives.get(includePath);
|
|
138569
|
+
if (directive) {
|
|
138570
|
+
this.state.setHeaderDirective(includePath, directive);
|
|
138571
|
+
}
|
|
138572
|
+
}
|
|
138203
138573
|
const cnextIncludeFiles = [];
|
|
138204
138574
|
IncludeTreeWalker_default.walk(
|
|
138205
138575
|
resolved.cnextIncludes,
|
|
@@ -138257,6 +138627,7 @@ var Transpiler = class {
|
|
|
138257
138627
|
this.state.reset();
|
|
138258
138628
|
CodeGenState.symbolTable.clear();
|
|
138259
138629
|
SymbolRegistry_default.reset();
|
|
138630
|
+
CodeGenState.callbackCompatibleFunctions = /* @__PURE__ */ new Set();
|
|
138260
138631
|
}
|
|
138261
138632
|
/**
|
|
138262
138633
|
* Ensure output directories exist
|
|
@@ -138335,8 +138706,12 @@ var Transpiler = class {
|
|
|
138335
138706
|
if (file.symbolOnly) {
|
|
138336
138707
|
continue;
|
|
138337
138708
|
}
|
|
138338
|
-
const
|
|
138339
|
-
if (
|
|
138709
|
+
const headerContent = this.generateHeaderForFile(file);
|
|
138710
|
+
if (headerContent) {
|
|
138711
|
+
const headerPath = this.pathResolver.getHeaderOutputPath(
|
|
138712
|
+
file.discoveredFile
|
|
138713
|
+
);
|
|
138714
|
+
this.fs.writeFile(headerPath, headerContent);
|
|
138340
138715
|
result.outputFiles.push(headerPath);
|
|
138341
138716
|
}
|
|
138342
138717
|
}
|
|
@@ -138370,7 +138745,7 @@ var Transpiler = class {
|
|
|
138370
138745
|
return result;
|
|
138371
138746
|
}
|
|
138372
138747
|
// ===========================================================================
|
|
138373
|
-
//
|
|
138748
|
+
// File Discovery (Stage 1 for files mode)
|
|
138374
138749
|
// ===========================================================================
|
|
138375
138750
|
/**
|
|
138376
138751
|
* Discover C-Next files from a single input (file or directory).
|
|
@@ -138431,6 +138806,10 @@ var Transpiler = class {
|
|
|
138431
138806
|
""
|
|
138432
138807
|
);
|
|
138433
138808
|
depGraph.addDependency(cnxPath, includePath);
|
|
138809
|
+
const directive = resolved.headerIncludeDirectives.get(includePath);
|
|
138810
|
+
if (directive) {
|
|
138811
|
+
this.state.setHeaderDirective(includePath, directive);
|
|
138812
|
+
}
|
|
138434
138813
|
const alreadyExists = cnextBaseNames.has(includeBaseName) || cnextFiles.some((f) => resolve12(f.path) === includePath);
|
|
138435
138814
|
if (!alreadyExists) {
|
|
138436
138815
|
cnextFiles.push(cnxInclude);
|
|
@@ -138494,14 +138873,14 @@ var Transpiler = class {
|
|
|
138494
138873
|
* This ensures headers are found based on what the source actually
|
|
138495
138874
|
* includes, not by blindly scanning include directories.
|
|
138496
138875
|
*/
|
|
138497
|
-
async
|
|
138876
|
+
async _discoverFromFiles() {
|
|
138498
138877
|
const cnextFiles = [];
|
|
138499
138878
|
const fileByPath = /* @__PURE__ */ new Map();
|
|
138500
138879
|
for (const input of this.config.inputs) {
|
|
138501
138880
|
this._discoverCNextFromInput(input, cnextFiles, fileByPath);
|
|
138502
138881
|
}
|
|
138503
138882
|
if (cnextFiles.length === 0) {
|
|
138504
|
-
return { cnextFiles: [], headerFiles: [] };
|
|
138883
|
+
return { cnextFiles: [], headerFiles: [], writeOutputToDisk: true };
|
|
138505
138884
|
}
|
|
138506
138885
|
const headerSet = /* @__PURE__ */ new Map();
|
|
138507
138886
|
const depGraph = new DependencyGraph_default();
|
|
@@ -138529,9 +138908,14 @@ var Transpiler = class {
|
|
|
138529
138908
|
}
|
|
138530
138909
|
);
|
|
138531
138910
|
this.warnings.push(...headerWarnings);
|
|
138911
|
+
const pipelineFiles = sortedCnextFiles.map((f) => ({
|
|
138912
|
+
path: f.path,
|
|
138913
|
+
discoveredFile: f
|
|
138914
|
+
}));
|
|
138532
138915
|
return {
|
|
138533
|
-
cnextFiles:
|
|
138534
|
-
headerFiles: allHeaders
|
|
138916
|
+
cnextFiles: pipelineFiles,
|
|
138917
|
+
headerFiles: allHeaders,
|
|
138918
|
+
writeOutputToDisk: true
|
|
138535
138919
|
};
|
|
138536
138920
|
}
|
|
138537
138921
|
// ===========================================================================
|
|
@@ -138716,17 +139100,22 @@ var Transpiler = class {
|
|
|
138716
139100
|
* Stage 6: Generate header file for a C-Next file
|
|
138717
139101
|
* ADR-055 Phase 7: Uses TSymbol directly, converts to IHeaderSymbol for generation.
|
|
138718
139102
|
*/
|
|
138719
|
-
|
|
138720
|
-
|
|
139103
|
+
/**
|
|
139104
|
+
* Generate header content for a single file's exported symbols.
|
|
139105
|
+
* Unified method replacing both generateHeader() and generateHeaderContent().
|
|
139106
|
+
* Reads all needed data from state (populated during Stage 5).
|
|
139107
|
+
*/
|
|
139108
|
+
generateHeaderForFile(file) {
|
|
139109
|
+
const sourcePath = file.path;
|
|
139110
|
+
const tSymbols = CodeGenState.symbolTable.getTSymbolsByFile(sourcePath);
|
|
138721
139111
|
const exportedSymbols = tSymbols.filter((s) => s.isExported);
|
|
138722
139112
|
if (exportedSymbols.length === 0) {
|
|
138723
139113
|
return null;
|
|
138724
139114
|
}
|
|
138725
|
-
const headerName = basename5(
|
|
138726
|
-
const
|
|
138727
|
-
const
|
|
138728
|
-
const
|
|
138729
|
-
const userIncludes = this.state.getUserIncludes(file.path);
|
|
139115
|
+
const headerName = basename5(sourcePath).replace(/\.cnx$|\.cnext$/, ".h");
|
|
139116
|
+
const typeInput = this.state.getSymbolInfo(sourcePath);
|
|
139117
|
+
const passByValueParams = this.state.getPassByValueParams(sourcePath) ?? /* @__PURE__ */ new Map();
|
|
139118
|
+
const userIncludes = this.state.getUserIncludes(sourcePath);
|
|
138730
139119
|
const allKnownEnums = TransitiveEnumCollector_default.aggregateKnownEnums(
|
|
138731
139120
|
this.state.getAllSymbolInfo()
|
|
138732
139121
|
);
|
|
@@ -138741,7 +139130,7 @@ var Transpiler = class {
|
|
|
138741
139130
|
unmodifiedParams,
|
|
138742
139131
|
allKnownEnums
|
|
138743
139132
|
);
|
|
138744
|
-
|
|
139133
|
+
return this.headerGenerator.generate(
|
|
138745
139134
|
headerSymbols,
|
|
138746
139135
|
headerName,
|
|
138747
139136
|
{
|
|
@@ -138754,8 +139143,6 @@ var Transpiler = class {
|
|
|
138754
139143
|
passByValueParams,
|
|
138755
139144
|
allKnownEnums
|
|
138756
139145
|
);
|
|
138757
|
-
this.fs.writeFile(headerPath, headerContent);
|
|
138758
|
-
return headerPath;
|
|
138759
139146
|
}
|
|
138760
139147
|
/**
|
|
138761
139148
|
* Collect external enum sources from included C-Next files.
|
|
@@ -138788,43 +139175,6 @@ var Transpiler = class {
|
|
|
138788
139175
|
);
|
|
138789
139176
|
}
|
|
138790
139177
|
}
|
|
138791
|
-
/**
|
|
138792
|
-
* Generate header content for exported symbols.
|
|
138793
|
-
* Issue #591: Extracted from transpileSource() for reduced complexity.
|
|
138794
|
-
* ADR-055 Phase 7: Works with TSymbol[], converts to IHeaderSymbol for generation.
|
|
138795
|
-
*/
|
|
138796
|
-
generateHeaderContent(tSymbols, sourcePath, cppMode, userIncludes, passByValueParams, symbolInfo) {
|
|
138797
|
-
const exportedSymbols = tSymbols.filter((s) => s.isExported);
|
|
138798
|
-
if (exportedSymbols.length === 0) {
|
|
138799
|
-
return void 0;
|
|
138800
|
-
}
|
|
138801
|
-
const unmodifiedParams = this.codeGenerator.getFunctionUnmodifiedParams();
|
|
138802
|
-
const headerSymbols = this.convertToHeaderSymbols(
|
|
138803
|
-
exportedSymbols,
|
|
138804
|
-
unmodifiedParams,
|
|
138805
|
-
symbolInfo.knownEnums
|
|
138806
|
-
);
|
|
138807
|
-
const headerName = basename5(sourcePath).replace(/\.cnx$|\.cnext$/, ".h");
|
|
138808
|
-
const typeInput = CodeGenState.symbols;
|
|
138809
|
-
const externalTypeHeaders = ExternalTypeHeaderBuilder_default.build(
|
|
138810
|
-
this.state.getAllHeaderDirectives(),
|
|
138811
|
-
CodeGenState.symbolTable
|
|
138812
|
-
);
|
|
138813
|
-
const typeInputWithSymbolTable = typeInput ? { ...typeInput, symbolTable: CodeGenState.symbolTable } : void 0;
|
|
138814
|
-
return this.headerGenerator.generate(
|
|
138815
|
-
headerSymbols,
|
|
138816
|
-
headerName,
|
|
138817
|
-
{
|
|
138818
|
-
exportedOnly: true,
|
|
138819
|
-
userIncludes,
|
|
138820
|
-
externalTypeHeaders,
|
|
138821
|
-
cppMode
|
|
138822
|
-
},
|
|
138823
|
-
typeInputWithSymbolTable,
|
|
138824
|
-
passByValueParams,
|
|
138825
|
-
symbolInfo.knownEnums
|
|
138826
|
-
);
|
|
138827
|
-
}
|
|
138828
139178
|
/**
|
|
138829
139179
|
* Convert TSymbols to IHeaderSymbols with auto-const information applied.
|
|
138830
139180
|
* ADR-055 Phase 7: Replaces mutation-based auto-const updating.
|
|
@@ -139032,7 +139382,7 @@ var Runner = class {
|
|
|
139032
139382
|
target: config.target,
|
|
139033
139383
|
debugMode: config.debugMode
|
|
139034
139384
|
});
|
|
139035
|
-
const result = await pipeline.
|
|
139385
|
+
const result = await pipeline.transpile({ kind: "files" });
|
|
139036
139386
|
this._renameOutputIfNeeded(result, explicitOutputFile);
|
|
139037
139387
|
ResultPrinter_default.print(result);
|
|
139038
139388
|
process.exit(result.success ? 0 : 1);
|
|
@@ -139742,16 +140092,31 @@ var ServeCommand = class _ServeCommand {
|
|
|
139742
140092
|
}
|
|
139743
140093
|
const { source, filePath } = params;
|
|
139744
140094
|
const options = filePath ? { workingDir: dirname11(filePath), sourcePath: filePath } : void 0;
|
|
139745
|
-
const
|
|
140095
|
+
const transpileResult = await _ServeCommand.transpiler.transpile({
|
|
140096
|
+
kind: "source",
|
|
139746
140097
|
source,
|
|
139747
|
-
options
|
|
139748
|
-
);
|
|
140098
|
+
...options
|
|
140099
|
+
});
|
|
140100
|
+
const fileResult = transpileResult.files.find(
|
|
140101
|
+
(f) => f.sourcePath === (filePath ?? "<string>")
|
|
140102
|
+
) ?? transpileResult.files[0];
|
|
140103
|
+
if (!fileResult) {
|
|
140104
|
+
return {
|
|
140105
|
+
success: true,
|
|
140106
|
+
result: {
|
|
140107
|
+
success: false,
|
|
140108
|
+
code: "",
|
|
140109
|
+
errors: transpileResult.errors,
|
|
140110
|
+
cppDetected: _ServeCommand.transpiler.isCppDetected()
|
|
140111
|
+
}
|
|
140112
|
+
};
|
|
140113
|
+
}
|
|
139749
140114
|
return {
|
|
139750
140115
|
success: true,
|
|
139751
140116
|
result: {
|
|
139752
|
-
success:
|
|
139753
|
-
code:
|
|
139754
|
-
errors:
|
|
140117
|
+
success: fileResult.success,
|
|
140118
|
+
code: fileResult.code,
|
|
140119
|
+
errors: fileResult.errors,
|
|
139755
140120
|
cppDetected: _ServeCommand.transpiler.isCppDetected()
|
|
139756
140121
|
}
|
|
139757
140122
|
};
|
|
@@ -139765,7 +140130,9 @@ var ServeCommand = class _ServeCommand {
|
|
|
139765
140130
|
const { source, filePath } = params;
|
|
139766
140131
|
if (_ServeCommand.transpiler && filePath) {
|
|
139767
140132
|
try {
|
|
139768
|
-
await _ServeCommand.transpiler.
|
|
140133
|
+
await _ServeCommand.transpiler.transpile({
|
|
140134
|
+
kind: "source",
|
|
140135
|
+
source,
|
|
139769
140136
|
workingDir: dirname11(filePath),
|
|
139770
140137
|
sourcePath: filePath
|
|
139771
140138
|
});
|