c-next 0.2.4 → 0.2.5
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/dist/index.js +331 -37
- package/dist/index.js.map +3 -3
- package/package.json +3 -1
- package/src/transpiler/Transpiler.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +112 -1
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +48 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +18 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +38 -10
- package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +63 -10
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
- package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
- package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
- package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
- package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
- package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
- package/src/transpiler/state/CodeGenState.ts +21 -2
- package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
- package/src/{transpiler/output/codegen/utils → utils}/__tests__/ExpressionUnwrapper.test.ts +2 -2
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.5",
|
|
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",
|
|
@@ -32,6 +32,8 @@ var package_default = {
|
|
|
32
32
|
"test:cli": "node scripts/test-cli.js",
|
|
33
33
|
"test:q": "tsx scripts/test.ts -q",
|
|
34
34
|
"test:update": "tsx scripts/test.ts --update",
|
|
35
|
+
"integration:transpile": "tsx scripts/test.ts --transpile-only",
|
|
36
|
+
"integration:execute": "tsx scripts/test.ts --execute-only",
|
|
35
37
|
analyze: "./scripts/static-analysis.sh",
|
|
36
38
|
clean: "rm -rf dist src/transpiler/logic/parser/grammar src/transpiler/logic/parser/c/grammar src/transpiler/logic/parser/cpp/grammar",
|
|
37
39
|
"prettier:check": "prettier --check .",
|
|
@@ -110764,8 +110766,12 @@ var CodeGenState = class {
|
|
|
110764
110766
|
static callbackTypes = /* @__PURE__ */ new Map();
|
|
110765
110767
|
/** Callback field types: "Struct.field" -> callbackTypeName */
|
|
110766
110768
|
static callbackFieldTypes = /* @__PURE__ */ new Map();
|
|
110767
|
-
/**
|
|
110768
|
-
|
|
110769
|
+
/**
|
|
110770
|
+
* Functions that are assigned to C callback typedefs.
|
|
110771
|
+
* Maps function name -> typedef name (e.g., "my_flush" -> "flush_cb_t")
|
|
110772
|
+
* Issue #895: We need the typedef name to look up parameter types.
|
|
110773
|
+
*/
|
|
110774
|
+
static callbackCompatibleFunctions = /* @__PURE__ */ new Map();
|
|
110769
110775
|
// ===========================================================================
|
|
110770
110776
|
// PASS-BY-VALUE ANALYSIS (Issue #269)
|
|
110771
110777
|
// ===========================================================================
|
|
@@ -111133,6 +111139,20 @@ var CodeGenState = class {
|
|
|
111133
111139
|
static getCallbackType(name) {
|
|
111134
111140
|
return this.callbackTypes.get(name);
|
|
111135
111141
|
}
|
|
111142
|
+
/**
|
|
111143
|
+
* Issue #895: Get the typedef type string for a C typedef by name.
|
|
111144
|
+
* Used to look up function pointer typedef signatures for callback-compatible functions.
|
|
111145
|
+
*
|
|
111146
|
+
* @param typedefName - Name of the typedef (e.g., "flush_cb_t")
|
|
111147
|
+
* @returns The type string (e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)") or undefined
|
|
111148
|
+
*/
|
|
111149
|
+
static getTypedefType(typedefName) {
|
|
111150
|
+
const symbol = this.symbolTable.getCSymbol(typedefName);
|
|
111151
|
+
if (symbol?.kind === "type") {
|
|
111152
|
+
return symbol.type;
|
|
111153
|
+
}
|
|
111154
|
+
return void 0;
|
|
111155
|
+
}
|
|
111136
111156
|
/**
|
|
111137
111157
|
* Check if a type name is a known C-Next function.
|
|
111138
111158
|
*/
|
|
@@ -112906,7 +112926,7 @@ var TYPE_RANGES = {
|
|
|
112906
112926
|
};
|
|
112907
112927
|
var TYPE_RANGES_default = TYPE_RANGES;
|
|
112908
112928
|
|
|
112909
|
-
// src/
|
|
112929
|
+
// src/utils/ExpressionUnwrapper.ts
|
|
112910
112930
|
var ExpressionUnwrapper = class {
|
|
112911
112931
|
/**
|
|
112912
112932
|
* Navigate from ExpressionContext to ShiftExpressionContext.
|
|
@@ -115744,6 +115764,7 @@ var generatePostfixExpression = (ctx, input, state, orchestrator) => {
|
|
|
115744
115764
|
const rootIdentifier = primary.IDENTIFIER()?.getText();
|
|
115745
115765
|
const paramInfo = rootIdentifier ? state.currentParameters.get(rootIdentifier) : null;
|
|
115746
115766
|
const isStructParam = paramInfo?.isStruct ?? false;
|
|
115767
|
+
const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
|
|
115747
115768
|
const hasSubscriptOps = ops.some((op) => op.expression().length > 0);
|
|
115748
115769
|
const isNonArrayParamWithSubscript = paramInfo && !paramInfo.isArray && !paramInfo.isStruct && hasSubscriptOps;
|
|
115749
115770
|
let result;
|
|
@@ -115764,6 +115785,7 @@ var generatePostfixExpression = (ctx, input, state, orchestrator) => {
|
|
|
115764
115785
|
const postfixCtx = {
|
|
115765
115786
|
rootIdentifier,
|
|
115766
115787
|
isStructParam,
|
|
115788
|
+
forcePointerSemantics,
|
|
115767
115789
|
input,
|
|
115768
115790
|
state,
|
|
115769
115791
|
orchestrator,
|
|
@@ -115855,6 +115877,7 @@ var handleMemberOp = (memberName, tracking, ctx) => {
|
|
|
115855
115877
|
memberName,
|
|
115856
115878
|
rootIdentifier: ctx.rootIdentifier,
|
|
115857
115879
|
isStructParam: ctx.isStructParam,
|
|
115880
|
+
forcePointerSemantics: ctx.forcePointerSemantics,
|
|
115858
115881
|
isGlobalAccess: tracking.isGlobalAccess,
|
|
115859
115882
|
isCppAccessChain: tracking.isCppAccessChain,
|
|
115860
115883
|
currentStructType: tracking.currentStructType,
|
|
@@ -116589,7 +116612,7 @@ var tryStructParamAccess = (ctx, orchestrator) => {
|
|
|
116589
116612
|
if (!ctx.isStructParam || ctx.result !== ctx.rootIdentifier) {
|
|
116590
116613
|
return null;
|
|
116591
116614
|
}
|
|
116592
|
-
const structParamSep = memberAccessChain_default.getStructParamSeparator({
|
|
116615
|
+
const structParamSep = ctx.forcePointerSemantics ? "->" : memberAccessChain_default.getStructParamSeparator({
|
|
116593
116616
|
cppMode: orchestrator.isCppMode()
|
|
116594
116617
|
});
|
|
116595
116618
|
const output = initializeMemberOutput(ctx);
|
|
@@ -124794,7 +124817,16 @@ var MemberSeparatorResolver = class _MemberSeparatorResolver {
|
|
|
124794
124817
|
/**
|
|
124795
124818
|
* Build the separator context for a member access chain
|
|
124796
124819
|
*/
|
|
124797
|
-
static buildContext(
|
|
124820
|
+
static buildContext(input, deps) {
|
|
124821
|
+
const {
|
|
124822
|
+
firstId,
|
|
124823
|
+
hasGlobal,
|
|
124824
|
+
hasThis,
|
|
124825
|
+
currentScope,
|
|
124826
|
+
isStructParam,
|
|
124827
|
+
isCppAccess,
|
|
124828
|
+
forcePointerSemantics
|
|
124829
|
+
} = input;
|
|
124798
124830
|
const isCrossScope = hasGlobal && (deps.isKnownScope(firstId) || deps.isKnownRegister(firstId));
|
|
124799
124831
|
const scopedRegName = hasThis && currentScope ? `${currentScope}_${firstId}` : null;
|
|
124800
124832
|
const isScopedRegister = scopedRegName !== null && deps.isKnownRegister(scopedRegName);
|
|
@@ -124804,7 +124836,8 @@ var MemberSeparatorResolver = class _MemberSeparatorResolver {
|
|
|
124804
124836
|
isStructParam,
|
|
124805
124837
|
isCppAccess,
|
|
124806
124838
|
scopedRegName,
|
|
124807
|
-
isScopedRegister
|
|
124839
|
+
isScopedRegister,
|
|
124840
|
+
forcePointerSemantics
|
|
124808
124841
|
};
|
|
124809
124842
|
}
|
|
124810
124843
|
/**
|
|
@@ -124815,6 +124848,9 @@ var MemberSeparatorResolver = class _MemberSeparatorResolver {
|
|
|
124815
124848
|
return "::";
|
|
124816
124849
|
}
|
|
124817
124850
|
if (ctx.isStructParam) {
|
|
124851
|
+
if (ctx.forcePointerSemantics) {
|
|
124852
|
+
return "->";
|
|
124853
|
+
}
|
|
124818
124854
|
return deps.getStructParamSeparator();
|
|
124819
124855
|
}
|
|
124820
124856
|
if (ctx.isCrossScope) {
|
|
@@ -124875,6 +124911,9 @@ var ParameterDereferenceResolver = class _ParameterDereferenceResolver {
|
|
|
124875
124911
|
* Determine if a parameter should be passed by value (no dereference needed)
|
|
124876
124912
|
*/
|
|
124877
124913
|
static isPassByValue(paramInfo, deps) {
|
|
124914
|
+
if (paramInfo.isCallbackPointerPrimitive) {
|
|
124915
|
+
return false;
|
|
124916
|
+
}
|
|
124878
124917
|
if (paramInfo.isCallback) {
|
|
124879
124918
|
return true;
|
|
124880
124919
|
}
|
|
@@ -124913,6 +124952,9 @@ var ParameterDereferenceResolver = class _ParameterDereferenceResolver {
|
|
|
124913
124952
|
if (_ParameterDereferenceResolver.isPassByValue(paramInfo, deps)) {
|
|
124914
124953
|
return id;
|
|
124915
124954
|
}
|
|
124955
|
+
if (paramInfo.forcePointerSemantics) {
|
|
124956
|
+
return `(*${id})`;
|
|
124957
|
+
}
|
|
124916
124958
|
return deps.maybeDereference(id);
|
|
124917
124959
|
}
|
|
124918
124960
|
};
|
|
@@ -125455,6 +125497,137 @@ var CastValidator = class _CastValidator {
|
|
|
125455
125497
|
};
|
|
125456
125498
|
var CastValidator_default = CastValidator;
|
|
125457
125499
|
|
|
125500
|
+
// src/transpiler/output/codegen/helpers/TypedefParamParser.ts
|
|
125501
|
+
var TypedefParamParser = class _TypedefParamParser {
|
|
125502
|
+
/**
|
|
125503
|
+
* Parse a function pointer typedef type string.
|
|
125504
|
+
*
|
|
125505
|
+
* @param typedefType - The type string, e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)"
|
|
125506
|
+
* @returns Parsed result with return type and parameters, or null if parsing fails
|
|
125507
|
+
*/
|
|
125508
|
+
static parse(typedefType) {
|
|
125509
|
+
const funcPtrIndex = typedefType.indexOf("(*)");
|
|
125510
|
+
if (funcPtrIndex === -1) {
|
|
125511
|
+
return null;
|
|
125512
|
+
}
|
|
125513
|
+
const returnType = typedefType.substring(0, funcPtrIndex).trim();
|
|
125514
|
+
const afterFuncPtr = typedefType.substring(funcPtrIndex + 3).trim();
|
|
125515
|
+
if (!afterFuncPtr.startsWith("(")) {
|
|
125516
|
+
return null;
|
|
125517
|
+
}
|
|
125518
|
+
const paramsStr = _TypedefParamParser.extractParenContent(afterFuncPtr);
|
|
125519
|
+
if (paramsStr === null) {
|
|
125520
|
+
return null;
|
|
125521
|
+
}
|
|
125522
|
+
if (!paramsStr || paramsStr === "void") {
|
|
125523
|
+
return { returnType, params: [] };
|
|
125524
|
+
}
|
|
125525
|
+
const paramStrings = _TypedefParamParser.splitParams(paramsStr);
|
|
125526
|
+
const params = paramStrings.map((p) => _TypedefParamParser.parseParam(p));
|
|
125527
|
+
return { returnType, params };
|
|
125528
|
+
}
|
|
125529
|
+
/**
|
|
125530
|
+
* Extract content between matching parentheses, handling arbitrary nesting.
|
|
125531
|
+
* @param str - String starting with '('
|
|
125532
|
+
* @returns Content between outer parens, or null if no match
|
|
125533
|
+
*/
|
|
125534
|
+
static extractParenContent(str) {
|
|
125535
|
+
if (!str.startsWith("(")) {
|
|
125536
|
+
return null;
|
|
125537
|
+
}
|
|
125538
|
+
let depth = 0;
|
|
125539
|
+
for (let i = 0; i < str.length; i++) {
|
|
125540
|
+
if (str[i] === "(") {
|
|
125541
|
+
depth++;
|
|
125542
|
+
} else if (str[i] === ")") {
|
|
125543
|
+
depth--;
|
|
125544
|
+
if (depth === 0) {
|
|
125545
|
+
return str.substring(1, i);
|
|
125546
|
+
}
|
|
125547
|
+
}
|
|
125548
|
+
}
|
|
125549
|
+
return null;
|
|
125550
|
+
}
|
|
125551
|
+
/**
|
|
125552
|
+
* Split parameter string by commas, respecting nested parentheses.
|
|
125553
|
+
*/
|
|
125554
|
+
static splitParams(paramsStr) {
|
|
125555
|
+
const params = [];
|
|
125556
|
+
let current = "";
|
|
125557
|
+
let depth = 0;
|
|
125558
|
+
for (const char of paramsStr) {
|
|
125559
|
+
if (char === "(") {
|
|
125560
|
+
depth++;
|
|
125561
|
+
current += char;
|
|
125562
|
+
} else if (char === ")") {
|
|
125563
|
+
depth--;
|
|
125564
|
+
current += char;
|
|
125565
|
+
} else if (char === "," && depth === 0) {
|
|
125566
|
+
params.push(current.trim());
|
|
125567
|
+
current = "";
|
|
125568
|
+
} else {
|
|
125569
|
+
current += char;
|
|
125570
|
+
}
|
|
125571
|
+
}
|
|
125572
|
+
if (current.trim()) {
|
|
125573
|
+
params.push(current.trim());
|
|
125574
|
+
}
|
|
125575
|
+
return params;
|
|
125576
|
+
}
|
|
125577
|
+
/**
|
|
125578
|
+
* Parse a single parameter type string.
|
|
125579
|
+
*/
|
|
125580
|
+
static parseParam(paramStr) {
|
|
125581
|
+
const trimmed = paramStr.trim();
|
|
125582
|
+
const isPointer = trimmed.includes("*");
|
|
125583
|
+
const isConst = /\bconst\b/.test(trimmed) || trimmed.startsWith("const");
|
|
125584
|
+
let baseType = trimmed.replaceAll(/\bconst\b/g, "").replace(/^const/, "").replaceAll("*", "").replaceAll(/\s+/g, " ").trim();
|
|
125585
|
+
if (baseType.includes(" ")) {
|
|
125586
|
+
baseType = baseType.replace(/\s+\w+$/, "");
|
|
125587
|
+
}
|
|
125588
|
+
if (baseType.startsWith("struct ")) {
|
|
125589
|
+
baseType = baseType.substring(7);
|
|
125590
|
+
}
|
|
125591
|
+
return {
|
|
125592
|
+
type: trimmed,
|
|
125593
|
+
isPointer,
|
|
125594
|
+
isConst,
|
|
125595
|
+
baseType
|
|
125596
|
+
};
|
|
125597
|
+
}
|
|
125598
|
+
/**
|
|
125599
|
+
* Get the parameter info at a given index, or null if not found.
|
|
125600
|
+
*/
|
|
125601
|
+
static getParamAt(typedefType, paramIndex) {
|
|
125602
|
+
const parsed = _TypedefParamParser.parse(typedefType);
|
|
125603
|
+
if (!parsed || paramIndex >= parsed.params.length) {
|
|
125604
|
+
return null;
|
|
125605
|
+
}
|
|
125606
|
+
return parsed.params[paramIndex];
|
|
125607
|
+
}
|
|
125608
|
+
/**
|
|
125609
|
+
* Check if a parameter at a given index should be a pointer based on the typedef.
|
|
125610
|
+
*
|
|
125611
|
+
* @param typedefType - The typedef type string
|
|
125612
|
+
* @param paramIndex - The parameter index (0-based)
|
|
125613
|
+
* @returns true if the param should be a pointer, false for value, null if unknown
|
|
125614
|
+
*/
|
|
125615
|
+
static shouldBePointer(typedefType, paramIndex) {
|
|
125616
|
+
return _TypedefParamParser.getParamAt(typedefType, paramIndex)?.isPointer ?? null;
|
|
125617
|
+
}
|
|
125618
|
+
/**
|
|
125619
|
+
* Check if a parameter at a given index should be const based on the typedef.
|
|
125620
|
+
*
|
|
125621
|
+
* @param typedefType - The typedef type string
|
|
125622
|
+
* @param paramIndex - The parameter index (0-based)
|
|
125623
|
+
* @returns true if the param should be const, false otherwise, null if unknown
|
|
125624
|
+
*/
|
|
125625
|
+
static shouldBeConst(typedefType, paramIndex) {
|
|
125626
|
+
return _TypedefParamParser.getParamAt(typedefType, paramIndex)?.isConst ?? null;
|
|
125627
|
+
}
|
|
125628
|
+
};
|
|
125629
|
+
var TypedefParamParser_default = TypedefParamParser;
|
|
125630
|
+
|
|
125458
125631
|
// src/transpiler/output/codegen/helpers/FunctionContextManager.ts
|
|
125459
125632
|
var FunctionContextManager = class _FunctionContextManager {
|
|
125460
125633
|
/**
|
|
@@ -125515,14 +125688,15 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125515
125688
|
static processParameterList(params, callbacks) {
|
|
125516
125689
|
CodeGenState.currentParameters.clear();
|
|
125517
125690
|
if (!params) return;
|
|
125518
|
-
|
|
125519
|
-
|
|
125691
|
+
const paramList = params.parameter();
|
|
125692
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
125693
|
+
_FunctionContextManager.processParameter(paramList[i], callbacks, i);
|
|
125520
125694
|
}
|
|
125521
125695
|
}
|
|
125522
125696
|
/**
|
|
125523
125697
|
* Process a single parameter declaration.
|
|
125524
125698
|
*/
|
|
125525
|
-
static processParameter(param, callbacks) {
|
|
125699
|
+
static processParameter(param, callbacks, paramIndex) {
|
|
125526
125700
|
const name = param.IDENTIFIER().getText();
|
|
125527
125701
|
const isArray = param.arrayDimension().length > 0 || param.type().arrayType() !== null;
|
|
125528
125702
|
const isConst = param.constModifier() !== null;
|
|
@@ -125531,17 +125705,21 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125531
125705
|
typeCtx,
|
|
125532
125706
|
callbacks
|
|
125533
125707
|
);
|
|
125534
|
-
const
|
|
125535
|
-
|
|
125536
|
-
|
|
125708
|
+
const callbackTypedefInfo = _FunctionContextManager.getCallbackTypedefParamInfo(paramIndex);
|
|
125709
|
+
const isCallbackPointerParam = callbackTypedefInfo?.shouldBePointer ?? false;
|
|
125710
|
+
const isStruct = callbackTypedefInfo ? isCallbackPointerParam && typeInfo.isStruct : typeInfo.isStruct;
|
|
125711
|
+
const isCallbackPointerPrimitive = isCallbackPointerParam && !typeInfo.isStruct && !isArray;
|
|
125537
125712
|
const paramInfo = {
|
|
125538
125713
|
name,
|
|
125539
125714
|
baseType: typeInfo.typeName,
|
|
125540
125715
|
isArray,
|
|
125541
|
-
isStruct
|
|
125716
|
+
isStruct,
|
|
125542
125717
|
isConst,
|
|
125543
125718
|
isCallback: typeInfo.isCallback,
|
|
125544
|
-
isString: typeInfo.isString
|
|
125719
|
+
isString: typeInfo.isString,
|
|
125720
|
+
isCallbackPointerPrimitive,
|
|
125721
|
+
// Issue #895: Callback-compatible params need pointer semantics even in C++ mode
|
|
125722
|
+
forcePointerSemantics: isCallbackPointerParam
|
|
125545
125723
|
};
|
|
125546
125724
|
CodeGenState.currentParameters.set(name, paramInfo);
|
|
125547
125725
|
_FunctionContextManager.registerParameterType(
|
|
@@ -125704,6 +125882,32 @@ var FunctionContextManager = class _FunctionContextManager {
|
|
|
125704
125882
|
}
|
|
125705
125883
|
return dimensions;
|
|
125706
125884
|
}
|
|
125885
|
+
/**
|
|
125886
|
+
* Issue #895: Get callback typedef parameter info from the C header.
|
|
125887
|
+
* Returns null if not callback-compatible or index is invalid.
|
|
125888
|
+
*/
|
|
125889
|
+
static getCallbackTypedefParamInfo(paramIndex) {
|
|
125890
|
+
if (CodeGenState.currentFunctionName === null) return null;
|
|
125891
|
+
const typedefName = CodeGenState.callbackCompatibleFunctions.get(
|
|
125892
|
+
CodeGenState.currentFunctionName
|
|
125893
|
+
);
|
|
125894
|
+
if (!typedefName) return null;
|
|
125895
|
+
const typedefType = CodeGenState.getTypedefType(typedefName);
|
|
125896
|
+
if (!typedefType) return null;
|
|
125897
|
+
const shouldBePointer = TypedefParamParser_default.shouldBePointer(
|
|
125898
|
+
typedefType,
|
|
125899
|
+
paramIndex
|
|
125900
|
+
);
|
|
125901
|
+
const shouldBeConst = TypedefParamParser_default.shouldBeConst(
|
|
125902
|
+
typedefType,
|
|
125903
|
+
paramIndex
|
|
125904
|
+
);
|
|
125905
|
+
if (shouldBePointer === null) return null;
|
|
125906
|
+
return {
|
|
125907
|
+
shouldBePointer,
|
|
125908
|
+
shouldBeConst: shouldBeConst ?? false
|
|
125909
|
+
};
|
|
125910
|
+
}
|
|
125707
125911
|
/**
|
|
125708
125912
|
* Extract string capacity from a string type context.
|
|
125709
125913
|
*/
|
|
@@ -126496,7 +126700,8 @@ var ParameterInputAdapter = class {
|
|
|
126496
126700
|
}
|
|
126497
126701
|
const isKnownStruct = deps.isKnownStruct(typeName);
|
|
126498
126702
|
const isKnownPrimitive = !!deps.typeMap[typeName];
|
|
126499
|
-
const isAutoConst = !deps.isModified && !isConst;
|
|
126703
|
+
const isAutoConst = !deps.isCallbackCompatible && !deps.isModified && !isConst;
|
|
126704
|
+
const isPassByReference = deps.forcePassByReference || isKnownStruct || isKnownPrimitive;
|
|
126500
126705
|
return {
|
|
126501
126706
|
name,
|
|
126502
126707
|
baseType: typeName,
|
|
@@ -126507,7 +126712,12 @@ var ParameterInputAdapter = class {
|
|
|
126507
126712
|
isCallback: false,
|
|
126508
126713
|
isString: false,
|
|
126509
126714
|
isPassByValue: deps.isPassByValue,
|
|
126510
|
-
isPassByReference
|
|
126715
|
+
isPassByReference,
|
|
126716
|
+
// Issue #895: Force pointer syntax in C++ mode for callback-compatible functions
|
|
126717
|
+
// because C callback typedefs expect pointers, not C++ references
|
|
126718
|
+
forcePointerSyntax: deps.forcePassByReference,
|
|
126719
|
+
// Issue #895: Preserve const from callback typedef signature
|
|
126720
|
+
forceConst: deps.forceConst
|
|
126511
126721
|
};
|
|
126512
126722
|
}
|
|
126513
126723
|
/**
|
|
@@ -126721,11 +126931,15 @@ var ParameterSignatureBuilder = class {
|
|
|
126721
126931
|
/**
|
|
126722
126932
|
* Build pass-by-reference parameter signature.
|
|
126723
126933
|
* C mode: const Point* p
|
|
126724
|
-
* C++ mode: const Point& p
|
|
126934
|
+
* C++ mode: const Point& p (unless forcePointerSyntax)
|
|
126935
|
+
*
|
|
126936
|
+
* Issue #895: When forcePointerSyntax is set, always use pointer syntax
|
|
126937
|
+
* because C callback typedefs expect pointers, not C++ references.
|
|
126725
126938
|
*/
|
|
126726
126939
|
static _buildRefParam(param, refSuffix) {
|
|
126727
126940
|
const constPrefix = this._getConstPrefix(param);
|
|
126728
|
-
|
|
126941
|
+
const actualSuffix = param.forcePointerSyntax ? "*" : refSuffix;
|
|
126942
|
+
return `${constPrefix}${param.mappedType}${actualSuffix} ${param.name}`;
|
|
126729
126943
|
}
|
|
126730
126944
|
/**
|
|
126731
126945
|
* Build unknown type parameter (pass by value, standard C semantics).
|
|
@@ -126735,13 +126949,15 @@ var ParameterSignatureBuilder = class {
|
|
|
126735
126949
|
return `${constMod}${param.mappedType} ${param.name}`;
|
|
126736
126950
|
}
|
|
126737
126951
|
/**
|
|
126738
|
-
* Get const prefix combining explicit const
|
|
126739
|
-
*
|
|
126952
|
+
* Get const prefix combining explicit const, auto-const, and forced const.
|
|
126953
|
+
* Priority: forceConst > isConst > isAutoConst
|
|
126954
|
+
* Issue #895: forceConst preserves const from callback typedef signature.
|
|
126740
126955
|
*/
|
|
126741
126956
|
static _getConstPrefix(param) {
|
|
126742
|
-
|
|
126743
|
-
|
|
126744
|
-
|
|
126957
|
+
if (param.forceConst || param.isConst || param.isAutoConst) {
|
|
126958
|
+
return "const ";
|
|
126959
|
+
}
|
|
126960
|
+
return "";
|
|
126745
126961
|
}
|
|
126746
126962
|
};
|
|
126747
126963
|
var ParameterSignatureBuilder_default = ParameterSignatureBuilder;
|
|
@@ -127717,7 +127933,7 @@ var CodeGenerator = class _CodeGenerator {
|
|
|
127717
127933
|
}
|
|
127718
127934
|
/** Generate parameter list for function signature */
|
|
127719
127935
|
generateParameterList(ctx) {
|
|
127720
|
-
return ctx.parameter().map((p) => this.generateParameter(p)).join(", ");
|
|
127936
|
+
return ctx.parameter().map((p, index) => this.generateParameter(p, index)).join(", ");
|
|
127721
127937
|
}
|
|
127722
127938
|
/** Get the raw type name without C conversion */
|
|
127723
127939
|
getTypeName(ctx) {
|
|
@@ -129463,13 +129679,17 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129463
129679
|
const typedef = this.generateCallbackTypedef(name);
|
|
129464
129680
|
return typedef ? functionCode + typedef : functionCode;
|
|
129465
129681
|
}
|
|
129466
|
-
generateParameter(ctx) {
|
|
129682
|
+
generateParameter(ctx, paramIndex) {
|
|
129467
129683
|
const typeName = this.getTypeName(ctx.type());
|
|
129468
129684
|
const name = ctx.IDENTIFIER().getText();
|
|
129469
129685
|
this._validateCStyleArrayParam(ctx, typeName, name);
|
|
129470
129686
|
this._validateUnboundedArrayParam(ctx);
|
|
129471
129687
|
const isModified = this._isCurrentParameterModified(name);
|
|
129472
|
-
const
|
|
129688
|
+
const callbackInfo = paramIndex === void 0 ? null : FunctionContextManager_default.getCallbackTypedefParamInfo(paramIndex);
|
|
129689
|
+
const isPassByValue = callbackInfo ? !callbackInfo.shouldBePointer : this._isPassByValueType(typeName, name);
|
|
129690
|
+
const isCallbackCompatible = callbackInfo !== null;
|
|
129691
|
+
const forcePassByReference = callbackInfo?.shouldBePointer ?? false;
|
|
129692
|
+
const forceConst = callbackInfo?.shouldBeConst ?? false;
|
|
129473
129693
|
const input = ParameterInputAdapter_default.fromAST(ctx, {
|
|
129474
129694
|
getTypeName: (t) => this.getTypeName(t),
|
|
129475
129695
|
generateType: (t) => this.generateType(t),
|
|
@@ -129478,7 +129698,10 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129478
129698
|
isKnownStruct: (t) => this.isKnownStruct(t),
|
|
129479
129699
|
typeMap: TYPE_MAP_default,
|
|
129480
129700
|
isModified,
|
|
129481
|
-
isPassByValue
|
|
129701
|
+
isPassByValue,
|
|
129702
|
+
isCallbackCompatible,
|
|
129703
|
+
forcePassByReference,
|
|
129704
|
+
forceConst
|
|
129482
129705
|
});
|
|
129483
129706
|
return ParameterSignatureBuilder_default.build(input, CppModeHelper_default.refOrPtr());
|
|
129484
129707
|
}
|
|
@@ -129816,14 +130039,18 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
|
|
|
129816
130039
|
const isStructParam = paramInfo?.isStruct ?? false;
|
|
129817
130040
|
const isCppAccess = hasGlobal && this.isCppScopeSymbol(firstId);
|
|
129818
130041
|
const separatorDeps = this._buildMemberSeparatorDeps();
|
|
130042
|
+
const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
|
|
129819
130043
|
const separatorCtx = MemberSeparatorResolver_default.buildContext(
|
|
129820
|
-
|
|
129821
|
-
|
|
129822
|
-
|
|
129823
|
-
|
|
129824
|
-
|
|
129825
|
-
|
|
129826
|
-
|
|
130044
|
+
{
|
|
130045
|
+
firstId,
|
|
130046
|
+
hasGlobal,
|
|
130047
|
+
hasThis,
|
|
130048
|
+
currentScope: CodeGenState.currentScope,
|
|
130049
|
+
isStructParam,
|
|
130050
|
+
isCppAccess,
|
|
130051
|
+
forcePointerSemantics
|
|
130052
|
+
},
|
|
130053
|
+
separatorDeps
|
|
129827
130054
|
);
|
|
129828
130055
|
return {
|
|
129829
130056
|
generateExpression: (expr) => this.generateExpression(expr),
|
|
@@ -136890,6 +137117,11 @@ var FunctionCallAnalyzer = class {
|
|
|
136890
137117
|
this.checkVarDeclForCallbackAssignment(varDecl);
|
|
136891
137118
|
return;
|
|
136892
137119
|
}
|
|
137120
|
+
const exprStmt = stmt.expressionStatement();
|
|
137121
|
+
if (exprStmt) {
|
|
137122
|
+
this.checkExpressionForCallbackArgs(exprStmt.expression());
|
|
137123
|
+
return;
|
|
137124
|
+
}
|
|
136893
137125
|
const ifStmt = stmt.ifStatement();
|
|
136894
137126
|
if (ifStmt) {
|
|
136895
137127
|
for (const child of ifStmt.statement()) {
|
|
@@ -136945,7 +137177,7 @@ var FunctionCallAnalyzer = class {
|
|
|
136945
137177
|
if (!funcRef) return;
|
|
136946
137178
|
const lookupName = funcRef.includes(".") ? funcRef.replace(".", "_") : funcRef;
|
|
136947
137179
|
if (this.allLocalFunctions.has(lookupName)) {
|
|
136948
|
-
CodeGenState.callbackCompatibleFunctions.
|
|
137180
|
+
CodeGenState.callbackCompatibleFunctions.set(lookupName, typeName);
|
|
136949
137181
|
}
|
|
136950
137182
|
}
|
|
136951
137183
|
/**
|
|
@@ -136961,6 +137193,68 @@ var FunctionCallAnalyzer = class {
|
|
|
136961
137193
|
}
|
|
136962
137194
|
return null;
|
|
136963
137195
|
}
|
|
137196
|
+
/**
|
|
137197
|
+
* Issue #895: Check expression for function calls that pass C-Next functions
|
|
137198
|
+
* to C function pointer parameters.
|
|
137199
|
+
*
|
|
137200
|
+
* Pattern: `global.widget_set_flush_cb(w, my_flush)`
|
|
137201
|
+
* Where widget_set_flush_cb's 2nd param is a C function pointer typedef.
|
|
137202
|
+
*/
|
|
137203
|
+
checkExpressionForCallbackArgs(expr) {
|
|
137204
|
+
const postfix = this.findPostfixExpression(expr);
|
|
137205
|
+
if (!postfix) return;
|
|
137206
|
+
const callInfo = this.extractCallInfo(postfix);
|
|
137207
|
+
if (!callInfo) return;
|
|
137208
|
+
const cFunc = this.symbolTable?.getCSymbol(callInfo.funcName);
|
|
137209
|
+
if (cFunc?.kind !== "function" || !cFunc.parameters) return;
|
|
137210
|
+
for (let i = 0; i < callInfo.args.length; i++) {
|
|
137211
|
+
const param = cFunc.parameters[i];
|
|
137212
|
+
if (!param) continue;
|
|
137213
|
+
if (!this.isCFunctionPointerTypedef(param.type)) continue;
|
|
137214
|
+
const funcRef = this.extractFunctionReference(callInfo.args[i]);
|
|
137215
|
+
if (!funcRef) continue;
|
|
137216
|
+
const lookupName = funcRef.includes(".") ? funcRef.replace(".", "_") : funcRef;
|
|
137217
|
+
if (this.allLocalFunctions.has(lookupName)) {
|
|
137218
|
+
CodeGenState.callbackCompatibleFunctions.set(lookupName, param.type);
|
|
137219
|
+
}
|
|
137220
|
+
}
|
|
137221
|
+
}
|
|
137222
|
+
/**
|
|
137223
|
+
* Find the postfix expression within an expression tree.
|
|
137224
|
+
* Uses ExpressionUnwrapper which validates that expression is "simple"
|
|
137225
|
+
* (single term at each level), returning null for complex expressions.
|
|
137226
|
+
*/
|
|
137227
|
+
findPostfixExpression(expr) {
|
|
137228
|
+
return ExpressionUnwrapper_default.getPostfixExpression(expr);
|
|
137229
|
+
}
|
|
137230
|
+
/**
|
|
137231
|
+
* Extract function name and arguments from a postfix expression.
|
|
137232
|
+
* Returns null if not a function call.
|
|
137233
|
+
*/
|
|
137234
|
+
extractCallInfo(postfix) {
|
|
137235
|
+
const primary = postfix.primaryExpression();
|
|
137236
|
+
const ops = postfix.postfixOp();
|
|
137237
|
+
const ident = primary.IDENTIFIER();
|
|
137238
|
+
const globalKw = primary.GLOBAL();
|
|
137239
|
+
if (!ident && !globalKw) {
|
|
137240
|
+
return null;
|
|
137241
|
+
}
|
|
137242
|
+
let funcName = ident ? ident.getText() : "";
|
|
137243
|
+
let argListOp = null;
|
|
137244
|
+
for (const op of ops) {
|
|
137245
|
+
if (op.IDENTIFIER()) {
|
|
137246
|
+
const member = op.IDENTIFIER().getText();
|
|
137247
|
+
funcName = funcName ? `${funcName}_${member}` : member;
|
|
137248
|
+
} else if (op.argumentList() || op.getText().startsWith("(")) {
|
|
137249
|
+
argListOp = op;
|
|
137250
|
+
break;
|
|
137251
|
+
}
|
|
137252
|
+
}
|
|
137253
|
+
if (!argListOp || !funcName) return null;
|
|
137254
|
+
const argList = argListOp.argumentList();
|
|
137255
|
+
const args = argList?.expression() ?? [];
|
|
137256
|
+
return { funcName, args };
|
|
137257
|
+
}
|
|
136964
137258
|
/**
|
|
136965
137259
|
* ADR-040: Register a variable that holds a callable (ISR or callback)
|
|
136966
137260
|
*/
|
|
@@ -138627,7 +138921,7 @@ var Transpiler = class {
|
|
|
138627
138921
|
this.state.reset();
|
|
138628
138922
|
CodeGenState.symbolTable.clear();
|
|
138629
138923
|
SymbolRegistry_default.reset();
|
|
138630
|
-
CodeGenState.callbackCompatibleFunctions = /* @__PURE__ */ new
|
|
138924
|
+
CodeGenState.callbackCompatibleFunctions = /* @__PURE__ */ new Map();
|
|
138631
138925
|
}
|
|
138632
138926
|
/**
|
|
138633
138927
|
* Ensure output directories exist
|