c-next 0.2.6 → 0.2.8

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.
Files changed (40) hide show
  1. package/dist/index.js +398 -433
  2. package/dist/index.js.map +4 -4
  3. package/package.json +1 -1
  4. package/src/transpiler/Transpiler.ts +50 -29
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -1
  6. package/src/transpiler/data/PathResolver.ts +10 -6
  7. package/src/transpiler/data/__tests__/PathResolver.test.ts +32 -0
  8. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +1 -12
  9. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +239 -0
  10. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +414 -0
  11. package/src/transpiler/logic/analysis/__tests__/StructFieldAnalyzer.test.ts +15 -75
  12. package/src/transpiler/logic/analysis/__tests__/runAnalyzers.test.ts +3 -15
  13. package/src/transpiler/logic/analysis/runAnalyzers.ts +11 -2
  14. package/src/transpiler/logic/analysis/types/ISignedShiftError.ts +15 -0
  15. package/src/transpiler/logic/symbols/SymbolUtils.ts +4 -1
  16. package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +10 -11
  17. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +4 -4
  18. package/src/transpiler/logic/symbols/cpp/__tests__/CppResolver.integration.test.ts +4 -4
  19. package/src/transpiler/output/codegen/CodeGenerator.ts +12 -4
  20. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
  21. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +26 -26
  22. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +12 -11
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +21 -21
  24. package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +7 -326
  25. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +20 -0
  26. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +18 -275
  27. package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +13 -1
  28. package/src/transpiler/output/codegen/generators/expressions/__tests__/AccessExprGenerator.test.ts +0 -573
  29. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +60 -0
  30. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +8 -439
  31. package/src/transpiler/output/codegen/generators/expressions/__tests__/UnaryExprGenerator.test.ts +196 -0
  32. package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +8 -4
  33. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +7 -2
  34. package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +34 -0
  35. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +48 -0
  36. package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +88 -0
  37. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +15 -2
  38. package/src/transpiler/output/headers/__tests__/BaseHeaderGenerator.test.ts +87 -0
  39. package/src/utils/ExpressionUtils.ts +51 -0
  40. package/src/utils/types/IParameterSymbol.ts +2 -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.6",
13
+ version: "0.2.8",
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",
@@ -770,18 +770,20 @@ var PathResolver = class {
770
770
  return join(dirname(file.path), outputName);
771
771
  }
772
772
  /**
773
- * Get output path for a header file (.h)
773
+ * Get output path for a header file (.h or .hpp)
774
774
  * Uses headerOutDir if specified, otherwise falls back to outDir
775
775
  *
776
776
  * @param file - The discovered file to get header path for
777
+ * @param cppMode - If true, output .hpp; otherwise .h (Issue #933)
777
778
  * @returns The full header output path
778
779
  */
779
- getHeaderOutputPath(file) {
780
+ getHeaderOutputPath(file, cppMode = false) {
781
+ const ext = cppMode ? ".hpp" : ".h";
780
782
  const headerDir = this.config.headerOutDir || this.config.outDir;
781
783
  const relativePath = this.getRelativePathFromInputs(file.path);
782
784
  if (relativePath) {
783
785
  const strippedPath = this.stripBasePath(relativePath);
784
- const outputRelative = strippedPath.replace(/\.cnx$|\.cnext$/, ".h");
786
+ const outputRelative = strippedPath.replace(/\.cnx$|\.cnext$/, ext);
785
787
  const outputPath = join(headerDir, outputRelative);
786
788
  const outputDir = dirname(outputPath);
787
789
  if (!this.fs.exists(outputDir)) {
@@ -794,7 +796,7 @@ var PathResolver = class {
794
796
  const relativeFromCwd = relative(cwd, file.path);
795
797
  if (relativeFromCwd && !relativeFromCwd.startsWith("..")) {
796
798
  const strippedPath = this.stripBasePath(relativeFromCwd);
797
- const outputRelative = strippedPath.replace(/\.cnx$|\.cnext$/, ".h");
799
+ const outputRelative = strippedPath.replace(/\.cnx$|\.cnext$/, ext);
798
800
  const outputPath2 = join(this.config.headerOutDir, outputRelative);
799
801
  const outputDir = dirname(outputPath2);
800
802
  if (!this.fs.exists(outputDir)) {
@@ -802,14 +804,14 @@ var PathResolver = class {
802
804
  }
803
805
  return outputPath2;
804
806
  }
805
- const headerName2 = basename2(file.path).replace(/\.cnx$|\.cnext$/, ".h");
807
+ const headerName2 = basename2(file.path).replace(/\.cnx$|\.cnext$/, ext);
806
808
  const outputPath = join(this.config.headerOutDir, headerName2);
807
809
  if (!this.fs.exists(this.config.headerOutDir)) {
808
810
  this.fs.mkdir(this.config.headerOutDir, { recursive: true });
809
811
  }
810
812
  return outputPath;
811
813
  }
812
- const headerName = basename2(file.path).replace(/\.cnx$|\.cnext$/, ".h");
814
+ const headerName = basename2(file.path).replace(/\.cnx$|\.cnext$/, ext);
813
815
  return join(dirname(file.path), headerName);
814
816
  }
815
817
  /**
@@ -113466,6 +113468,38 @@ var ExpressionUtils = class _ExpressionUtils {
113466
113468
  const identifier = primary.IDENTIFIER();
113467
113469
  return identifier?.getText() ?? null;
113468
113470
  }
113471
+ /**
113472
+ * Collect all additive expressions from a ternary expression.
113473
+ *
113474
+ * Uses flatMap chains to traverse the expression grammar efficiently.
113475
+ * Useful for analyzers that need to examine operands at the additive level.
113476
+ *
113477
+ * @param ctx - The ternary expression context
113478
+ * @returns Array of all additive expression contexts in the tree
113479
+ */
113480
+ static collectAdditiveExpressions(ctx) {
113481
+ return _ExpressionUtils.collectAdditiveFromOrExprs(ctx.orExpression());
113482
+ }
113483
+ /**
113484
+ * Collect additive expressions from an array of orExpression contexts.
113485
+ *
113486
+ * Internal helper that performs the actual flatMap traversal.
113487
+ */
113488
+ static collectAdditiveFromOrExprs(orExprs) {
113489
+ return orExprs.flatMap((or) => or.andExpression()).flatMap((and) => and.equalityExpression()).flatMap((eq) => eq.relationalExpression()).flatMap((rel) => rel.bitwiseOrExpression()).flatMap((bor) => bor.bitwiseXorExpression()).flatMap((bxor) => bxor.bitwiseAndExpression()).flatMap((band) => band.shiftExpression()).flatMap((shift) => shift.additiveExpression());
113490
+ }
113491
+ /**
113492
+ * Collect all unary expressions from an orExpression context.
113493
+ *
113494
+ * Traverses through the entire expression hierarchy to find all unary expressions.
113495
+ * Useful for analyzers that need to examine leaf operands.
113496
+ *
113497
+ * @param orExpr - The orExpression context
113498
+ * @returns Array of all unary expression contexts in the tree
113499
+ */
113500
+ static collectUnaryFromOrExpr(orExpr) {
113501
+ return _ExpressionUtils.collectAdditiveFromOrExprs([orExpr]).flatMap((add) => add.multiplicativeExpression()).flatMap((mul) => mul.unaryExpression());
113502
+ }
113469
113503
  /**
113470
113504
  * ADR-023: Check if an expression contains a function call anywhere in its tree.
113471
113505
  *
@@ -114776,6 +114810,95 @@ var binaryExprGenerators = {
114776
114810
  };
114777
114811
  var BinaryExprGenerator_default = binaryExprGenerators;
114778
114812
 
114813
+ // src/transpiler/output/codegen/helpers/CppModeHelper.ts
114814
+ var CppModeHelper = class {
114815
+ /**
114816
+ * Get address-of expression for struct parameter passing.
114817
+ * C mode: `&expr` (pass pointer to struct)
114818
+ * C++ mode: `expr` (pass reference directly)
114819
+ *
114820
+ * @param expr - The expression to potentially wrap
114821
+ * @returns The expression with address-of operator in C mode
114822
+ */
114823
+ static maybeAddressOf(expr) {
114824
+ return CodeGenState.cppMode ? expr : `&${expr}`;
114825
+ }
114826
+ /**
114827
+ * Get dereference expression for struct parameter access.
114828
+ * C mode: `(*expr)` (dereference pointer)
114829
+ * C++ mode: `expr` (reference can be used directly)
114830
+ *
114831
+ * @param expr - The expression to potentially dereference
114832
+ * @returns The expression with dereference in C mode
114833
+ */
114834
+ static maybeDereference(expr) {
114835
+ return CodeGenState.cppMode ? expr : `(*${expr})`;
114836
+ }
114837
+ /**
114838
+ * Get the type modifier for struct parameter declarations.
114839
+ * C mode: `*` (pointer type)
114840
+ * C++ mode: `&` (reference type)
114841
+ *
114842
+ * @returns The type modifier character
114843
+ */
114844
+ static refOrPtr() {
114845
+ return CodeGenState.cppMode ? "&" : "*";
114846
+ }
114847
+ /**
114848
+ * Get the member access separator for struct parameters.
114849
+ * C mode: `->` (pointer member access)
114850
+ * C++ mode: `.` (reference member access)
114851
+ *
114852
+ * @returns The member access separator
114853
+ */
114854
+ static memberSeparator() {
114855
+ return CodeGenState.cppMode ? "." : "->";
114856
+ }
114857
+ /**
114858
+ * Get NULL literal for the current mode.
114859
+ * C mode: `NULL`
114860
+ * C++ mode: `nullptr`
114861
+ *
114862
+ * @returns The null pointer literal
114863
+ */
114864
+ static nullLiteral() {
114865
+ return CodeGenState.cppMode ? "nullptr" : "NULL";
114866
+ }
114867
+ /**
114868
+ * Generate a cast expression for the current mode.
114869
+ * C mode: `(type)expr`
114870
+ * C++ mode: `static_cast<type>(expr)`
114871
+ *
114872
+ * @param type - The target type
114873
+ * @param expr - The expression to cast
114874
+ * @returns The cast expression
114875
+ */
114876
+ static cast(type, expr) {
114877
+ return CodeGenState.cppMode ? `static_cast<${type}>(${expr})` : `(${type})${expr}`;
114878
+ }
114879
+ /**
114880
+ * Generate a reinterpret cast expression for the current mode.
114881
+ * C mode: `(type)expr`
114882
+ * C++ mode: `reinterpret_cast<type>(expr)`
114883
+ *
114884
+ * @param type - The target type
114885
+ * @param expr - The expression to cast
114886
+ * @returns The cast expression
114887
+ */
114888
+ static reinterpretCast(type, expr) {
114889
+ return CodeGenState.cppMode ? `reinterpret_cast<${type}>(${expr})` : `(${type})${expr}`;
114890
+ }
114891
+ /**
114892
+ * Check if we're in C++ mode.
114893
+ *
114894
+ * @returns True if generating C++ code
114895
+ */
114896
+ static isCppMode() {
114897
+ return CodeGenState.cppMode;
114898
+ }
114899
+ };
114900
+ var CppModeHelper_default = CppModeHelper;
114901
+
114779
114902
  // src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts
114780
114903
  var generateUnaryExpr = (node, _input, _state, orchestrator) => {
114781
114904
  if (node.postfixExpression()) {
@@ -114788,7 +114911,16 @@ var generateUnaryExpr = (node, _input, _state, orchestrator) => {
114788
114911
  const text = node.getText();
114789
114912
  if (text.startsWith("!")) return { code: `!${inner}`, effects: [] };
114790
114913
  if (text.startsWith("-")) return { code: `-${inner}`, effects: [] };
114791
- if (text.startsWith("~")) return { code: `~${inner}`, effects: [] };
114914
+ if (text.startsWith("~")) {
114915
+ const innerType = TypeResolver_default2.getUnaryExpressionType(
114916
+ node.unaryExpression()
114917
+ );
114918
+ if (innerType && TypeResolver_default2.isUnsignedType(innerType)) {
114919
+ const cType = TYPE_MAP_default[innerType] ?? innerType;
114920
+ return { code: CppModeHelper_default.cast(cType, `~${inner}`), effects: [] };
114921
+ }
114922
+ return { code: `~${inner}`, effects: [] };
114923
+ }
114792
114924
  if (text.startsWith("&")) return { code: `&${inner}`, effects: [] };
114793
114925
  return { code: inner, effects: [] };
114794
114926
  };
@@ -114828,170 +114960,6 @@ var expressionGenerators = {
114828
114960
  var ExpressionGenerator_default = expressionGenerators;
114829
114961
 
114830
114962
  // src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts
114831
- var TYPE_WIDTH2 = {
114832
- u8: 8,
114833
- u16: 16,
114834
- u32: 32,
114835
- u64: 64,
114836
- i8: 8,
114837
- i16: 16,
114838
- i32: 32,
114839
- i64: 64,
114840
- f32: 32,
114841
- f64: 64,
114842
- bool: 1
114843
- };
114844
- var C_TYPE_WIDTH = {
114845
- uint8_t: 8,
114846
- uint16_t: 16,
114847
- uint32_t: 32,
114848
- uint64_t: 64,
114849
- int8_t: 8,
114850
- int16_t: 16,
114851
- int32_t: 32,
114852
- int64_t: 64,
114853
- float: 32,
114854
- double: 64
114855
- };
114856
- function getTypeBitWidth(typeName) {
114857
- return TYPE_WIDTH2[typeName] || C_TYPE_WIDTH[typeName] || 0;
114858
- }
114859
- function makeStrlenResult(expr, skipContinue) {
114860
- return {
114861
- code: `strlen(${expr})`,
114862
- effects: [{ type: "include", header: "string" }],
114863
- skipContinue
114864
- };
114865
- }
114866
- function makeBitWidthResult(typeName, skipContinue, isElement = false) {
114867
- const bitWidth = getTypeBitWidth(typeName);
114868
- if (bitWidth > 0) {
114869
- return { code: String(bitWidth), effects: [], skipContinue };
114870
- }
114871
- const typeLabel = isElement ? "element type" : "type";
114872
- return {
114873
- code: `/* .length: unsupported ${typeLabel} ${typeName} */0`,
114874
- effects: [],
114875
- skipContinue
114876
- };
114877
- }
114878
- function handleStructMemberLength(ctx) {
114879
- if (!ctx.previousStructType || !ctx.previousMemberName) {
114880
- return null;
114881
- }
114882
- const fieldInfo = ctx.getStructFieldInfo(
114883
- ctx.previousStructType,
114884
- ctx.previousMemberName
114885
- );
114886
- if (!fieldInfo) {
114887
- return null;
114888
- }
114889
- const { type: memberType, dimensions } = fieldInfo;
114890
- const isStringField = memberType.startsWith("string<");
114891
- if (dimensions && dimensions.length > 1 && isStringField) {
114892
- if (ctx.subscriptDepth === 0) {
114893
- return { code: String(dimensions[0]), effects: [], skipContinue: true };
114894
- }
114895
- return makeStrlenResult(ctx.result, true);
114896
- }
114897
- if (dimensions?.length === 1 && isStringField) {
114898
- return makeStrlenResult(ctx.result, true);
114899
- }
114900
- if (dimensions && dimensions.length > 0 && ctx.subscriptDepth < dimensions.length) {
114901
- return {
114902
- code: String(dimensions[ctx.subscriptDepth]),
114903
- effects: [],
114904
- skipContinue: true
114905
- };
114906
- }
114907
- if (dimensions && dimensions.length > 0 && ctx.subscriptDepth >= dimensions.length) {
114908
- return makeBitWidthResult(memberType, true, true);
114909
- }
114910
- return makeBitWidthResult(memberType, true, false);
114911
- }
114912
- function handleStringLength(ctx, typeInfo) {
114913
- if (typeInfo.arrayDimensions && typeInfo.arrayDimensions.length > 1) {
114914
- if (ctx.subscriptDepth === 0) {
114915
- return {
114916
- code: String(typeInfo.arrayDimensions[0]),
114917
- effects: [],
114918
- skipContinue: false
114919
- };
114920
- }
114921
- return makeStrlenResult(ctx.result, false);
114922
- }
114923
- if (ctx.currentIdentifier && ctx.lengthCache?.has(ctx.currentIdentifier)) {
114924
- return {
114925
- code: ctx.lengthCache.get(ctx.currentIdentifier),
114926
- effects: [],
114927
- skipContinue: false
114928
- };
114929
- }
114930
- const target = ctx.currentIdentifier ?? ctx.result;
114931
- return makeStrlenResult(target, false);
114932
- }
114933
- function handleFullySubscriptedArrayLength(ctx, typeInfo) {
114934
- if (typeInfo.isEnum) {
114935
- return { code: "32", effects: [], skipContinue: false };
114936
- }
114937
- if (typeInfo.baseType.startsWith("string<") || typeInfo.isString) {
114938
- return makeStrlenResult(ctx.result, false);
114939
- }
114940
- let elementBitWidth = getTypeBitWidth(typeInfo.baseType);
114941
- if (elementBitWidth === 0 && typeInfo.isBitmap && typeInfo.bitmapTypeName && ctx.getBitmapBitWidth) {
114942
- elementBitWidth = ctx.getBitmapBitWidth(typeInfo.bitmapTypeName) || 0;
114943
- }
114944
- if (elementBitWidth > 0) {
114945
- return { code: String(elementBitWidth), effects: [], skipContinue: false };
114946
- }
114947
- return {
114948
- code: `/* .length: unsupported element type ${typeInfo.baseType} */0`,
114949
- effects: [],
114950
- skipContinue: false
114951
- };
114952
- }
114953
- var generateLengthProperty = (ctx) => {
114954
- if (ctx.mainArgsName && ctx.primaryId === ctx.mainArgsName) {
114955
- return { code: "argc", effects: [], skipContinue: false };
114956
- }
114957
- const structResult = handleStructMemberLength(ctx);
114958
- if (structResult) {
114959
- return structResult;
114960
- }
114961
- const typeInfo = ctx.typeInfo;
114962
- if (!typeInfo) {
114963
- return {
114964
- code: `/* .length: unknown type for ${ctx.result} */0`,
114965
- effects: [],
114966
- skipContinue: false
114967
- };
114968
- }
114969
- if (typeInfo.isString) {
114970
- return handleStringLength(ctx, typeInfo);
114971
- }
114972
- const hasDimensions = typeInfo.isArray && typeInfo.arrayDimensions && typeInfo.arrayDimensions.length > 0;
114973
- if (hasDimensions && ctx.subscriptDepth < typeInfo.arrayDimensions.length) {
114974
- return {
114975
- code: String(typeInfo.arrayDimensions[ctx.subscriptDepth]),
114976
- effects: [],
114977
- skipContinue: false
114978
- };
114979
- }
114980
- if (hasDimensions && ctx.subscriptDepth >= typeInfo.arrayDimensions.length) {
114981
- return handleFullySubscriptedArrayLength(ctx, typeInfo);
114982
- }
114983
- if (typeInfo.isArray) {
114984
- return {
114985
- code: `/* .length unknown for ${ctx.currentIdentifier} */0`,
114986
- effects: [],
114987
- skipContinue: false
114988
- };
114989
- }
114990
- if (typeInfo.isEnum) {
114991
- return { code: "32", effects: [], skipContinue: false };
114992
- }
114993
- return { code: String(typeInfo.bitWidth), effects: [], skipContinue: false };
114994
- };
114995
114963
  var generateCapacityProperty = (typeInfo) => {
114996
114964
  if (typeInfo?.isString && typeInfo.stringCapacity !== void 0) {
114997
114965
  return { code: String(typeInfo.stringCapacity), effects: [] };
@@ -115016,7 +114984,6 @@ var generateBitmapFieldAccess = (result, fieldInfo) => {
115016
114984
  }
115017
114985
  };
115018
114986
  var accessGenerators = {
115019
- generateLengthProperty,
115020
114987
  generateCapacityProperty,
115021
114988
  generateSizeProperty,
115022
114989
  generateBitmapFieldAccess
@@ -115104,7 +115071,7 @@ var CallExprUtils = class _CallExprUtils {
115104
115071
  var CallExprUtils_default = CallExprUtils;
115105
115072
 
115106
115073
  // src/transpiler/output/codegen/types/C_TYPE_WIDTH.ts
115107
- var C_TYPE_WIDTH2 = {
115074
+ var C_TYPE_WIDTH = {
115108
115075
  uint8_t: 8,
115109
115076
  int8_t: 8,
115110
115077
  uint16_t: 16,
@@ -115132,7 +115099,7 @@ var C_TYPE_WIDTH2 = {
115132
115099
  "long long": 64,
115133
115100
  "unsigned long long": 64
115134
115101
  };
115135
- var C_TYPE_WIDTH_default = C_TYPE_WIDTH2;
115102
+ var C_TYPE_WIDTH_default = C_TYPE_WIDTH;
115136
115103
 
115137
115104
  // src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts
115138
115105
  var wrapWithCppEnumCast = (argCode, argExpr, targetParamBaseType, orchestrator) => {
@@ -115160,6 +115127,17 @@ var _parameterExpectsAddressOf = (paramType, argType, orchestrator) => {
115160
115127
  return paramBaseType === argType;
115161
115128
  };
115162
115129
  var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115130
+ const argIdentifier = orchestrator.getSimpleIdentifier(e);
115131
+ const paramInfo = argIdentifier ? CodeGenState.currentParameters.get(argIdentifier) : void 0;
115132
+ const isCallbackPromotedParam = paramInfo?.forcePointerSemantics ?? false;
115133
+ if (targetParam?.baseType?.endsWith("*") && isCallbackPromotedParam) {
115134
+ return wrapWithCppEnumCast(
115135
+ argIdentifier,
115136
+ e,
115137
+ targetParam?.baseType,
115138
+ orchestrator
115139
+ );
115140
+ }
115163
115141
  let argCode = orchestrator.generateExpression(e);
115164
115142
  if (!targetParam?.baseType?.endsWith("*")) {
115165
115143
  return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
@@ -115928,7 +115906,7 @@ var handleGlobalPrefix = (memberName, tracking, input, state, orchestrator) => {
115928
115906
  }
115929
115907
  return true;
115930
115908
  };
115931
- var handleThisScopeLength = (memberName, tracking, input, state, orchestrator) => {
115909
+ var handleThisScopeLength = (memberName, tracking, _input, state, orchestrator) => {
115932
115910
  if (tracking.result !== "__THIS_SCOPE__" || memberName !== "length") {
115933
115911
  return false;
115934
115912
  }
@@ -116017,19 +115995,9 @@ var tryExplicitLengthProperty = (memberName, tracking, rootIdentifier, input, st
116017
115995
  };
116018
115996
  var tryPropertyAccess = (memberName, tracking, rootIdentifier, input, state, orchestrator, effects) => {
116019
115997
  if (memberName === "length") {
116020
- const ctx = createExplicitLengthContext(tracking, rootIdentifier);
116021
- const lengthResult = generateLengthProperty2(
116022
- ctx,
116023
- input,
116024
- state,
116025
- orchestrator,
116026
- effects
115998
+ throw new Error(
115999
+ `Error: '.length' on '${tracking.result}' is deprecated. Use explicit properties: .bit_length (bit width), .byte_length (byte size), .element_count (array size), or .char_count (string length)`
116027
116000
  );
116028
- if (lengthResult !== null) {
116029
- applyPropertyResult(tracking, lengthResult);
116030
- return true;
116031
- }
116032
- return false;
116033
116001
  }
116034
116002
  const explicitProps = /* @__PURE__ */ new Set([
116035
116003
  "bit_length",
@@ -116074,129 +116042,6 @@ var tryPropertyAccess = (memberName, tracking, rootIdentifier, input, state, orc
116074
116042
  }
116075
116043
  return false;
116076
116044
  };
116077
- var generateLengthProperty2 = (ctx, input, state, orchestrator, effects) => {
116078
- if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
116079
- return "argc";
116080
- }
116081
- if (ctx.previousStructType && ctx.previousMemberName) {
116082
- const fieldInfo = orchestrator.getStructFieldInfo(
116083
- ctx.previousStructType,
116084
- ctx.previousMemberName
116085
- );
116086
- if (fieldInfo) {
116087
- return generateStructFieldLength(
116088
- ctx.result,
116089
- fieldInfo,
116090
- ctx.subscriptDepth,
116091
- input,
116092
- orchestrator,
116093
- effects
116094
- );
116095
- }
116096
- }
116097
- const typeInfo = ctx.resolvedIdentifier ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier) : void 0;
116098
- if (!typeInfo) {
116099
- return `/* .length: unknown type for ${ctx.result} */0`;
116100
- }
116101
- return generateTypeInfoLength(
116102
- ctx.result,
116103
- typeInfo,
116104
- ctx.subscriptDepth,
116105
- ctx.resolvedIdentifier,
116106
- input,
116107
- state,
116108
- effects
116109
- );
116110
- };
116111
- var generateStructFieldLength = (result, fieldInfo, subscriptDepth, input, orchestrator, effects) => {
116112
- const memberType = fieldInfo.type;
116113
- const dimensions = fieldInfo.dimensions;
116114
- const isStringField = TypeCheckUtils_default.isString(memberType);
116115
- if (dimensions?.length && dimensions.length > 1 && isStringField) {
116116
- if (subscriptDepth === 0) {
116117
- return String(dimensions[0]);
116118
- } else {
116119
- effects.push({ type: "include", header: "string" });
116120
- return `strlen(${result})`;
116121
- }
116122
- } else if (dimensions?.length === 1 && isStringField) {
116123
- effects.push({ type: "include", header: "string" });
116124
- return `strlen(${result})`;
116125
- } else if (dimensions?.length && dimensions.length > 0 && subscriptDepth < dimensions.length) {
116126
- return String(dimensions[subscriptDepth]);
116127
- } else if (dimensions?.length && dimensions.length > 0 && subscriptDepth >= dimensions.length) {
116128
- return getTypeBitWidth2(memberType, input);
116129
- } else {
116130
- return getTypeBitWidth2(memberType, input);
116131
- }
116132
- };
116133
- var generateTypeInfoLength = (result, typeInfo, subscriptDepth, resolvedIdentifier, input, state, effects) => {
116134
- if (typeInfo.isString) {
116135
- return generateStringLength(
116136
- result,
116137
- typeInfo,
116138
- subscriptDepth,
116139
- resolvedIdentifier,
116140
- state
116141
- );
116142
- }
116143
- if (typeInfo.isEnum && !typeInfo.isArray) {
116144
- return "32";
116145
- }
116146
- if (!typeInfo.isArray) {
116147
- return String(typeInfo.bitWidth || 0);
116148
- }
116149
- const dims = typeInfo.arrayDimensions;
116150
- if (!dims || dims.length === 0) {
116151
- return `/* .length unknown for ${resolvedIdentifier} */0`;
116152
- }
116153
- if (subscriptDepth < dims.length) {
116154
- return String(dims[subscriptDepth]);
116155
- }
116156
- return generateElementTypeLength(result, typeInfo, input, effects);
116157
- };
116158
- var generateStringLength = (result, typeInfo, subscriptDepth, resolvedIdentifier, state) => {
116159
- const dims = typeInfo.arrayDimensions;
116160
- if (dims && dims.length > 1) {
116161
- return subscriptDepth === 0 ? String(dims[0]) : `strlen(${result})`;
116162
- }
116163
- if (subscriptDepth > 0) {
116164
- return `strlen(${result})`;
116165
- }
116166
- if (resolvedIdentifier && state.lengthCache?.has(resolvedIdentifier)) {
116167
- return state.lengthCache.get(resolvedIdentifier);
116168
- }
116169
- return resolvedIdentifier ? `strlen(${resolvedIdentifier})` : `strlen(${result})`;
116170
- };
116171
- var generateElementTypeLength = (result, typeInfo, input, effects) => {
116172
- if (typeInfo.isEnum) {
116173
- return "32";
116174
- }
116175
- if (TypeCheckUtils_default.isString(typeInfo.baseType) || typeInfo.isString) {
116176
- effects.push({ type: "include", header: "string" });
116177
- return `strlen(${result})`;
116178
- }
116179
- let elementBitWidth = TYPE_WIDTH_default[typeInfo.baseType] || 0;
116180
- if (elementBitWidth === 0 && typeInfo.isBitmap && typeInfo.bitmapTypeName) {
116181
- elementBitWidth = input.symbols.bitmapBitWidth.get(typeInfo.bitmapTypeName) || 0;
116182
- }
116183
- if (elementBitWidth > 0) {
116184
- return String(elementBitWidth);
116185
- }
116186
- return `/* .length: unsupported element type ${typeInfo.baseType} */0`;
116187
- };
116188
- var getTypeBitWidth2 = (typeName, input) => {
116189
- let bitWidth = TYPE_WIDTH_default[typeName] || C_TYPE_WIDTH_default[typeName] || 0;
116190
- if (bitWidth === 0 && input.symbolTable) {
116191
- const enumWidth = input.symbolTable.getEnumBitWidth(typeName);
116192
- if (enumWidth) bitWidth = enumWidth;
116193
- }
116194
- if (bitWidth > 0) {
116195
- return String(bitWidth);
116196
- } else {
116197
- return `/* .length: unsupported type ${typeName} */0`;
116198
- }
116199
- };
116200
116045
  var getNumericBitWidth = (typeName, input) => {
116201
116046
  let bitWidth = TYPE_WIDTH_default[typeName] ?? C_TYPE_WIDTH_default[typeName] ?? 0;
116202
116047
  if (bitWidth === 0 && input.symbolTable) {
@@ -116472,11 +116317,10 @@ var generateCharCountProperty = (ctx, input, state, orchestrator, effects) => {
116472
116317
  );
116473
116318
  }
116474
116319
  effects.push({ type: "include", header: "string" });
116475
- if (ctx.resolvedIdentifier && state.lengthCache?.has(ctx.resolvedIdentifier)) {
116320
+ if (ctx.subscriptDepth === 0 && ctx.resolvedIdentifier && state.lengthCache?.has(ctx.resolvedIdentifier)) {
116476
116321
  return state.lengthCache.get(ctx.resolvedIdentifier);
116477
116322
  }
116478
- const target = ctx.resolvedIdentifier ?? ctx.result;
116479
- return `strlen(${target})`;
116323
+ return `strlen(${ctx.result})`;
116480
116324
  };
116481
116325
  var initializeMemberOutput = (ctx) => ({
116482
116326
  result: ctx.result,
@@ -122371,7 +122215,7 @@ var AssignmentContextBuilder_default = buildAssignmentContext;
122371
122215
  // src/transpiler/output/codegen/analysis/StringLengthCounter.ts
122372
122216
  var StringLengthCounter = class _StringLengthCounter {
122373
122217
  /**
122374
- * Count .length accesses in an expression.
122218
+ * Count .char_count accesses in an expression.
122375
122219
  */
122376
122220
  static countExpression(ctx) {
122377
122221
  const counts = /* @__PURE__ */ new Map();
@@ -122379,7 +122223,7 @@ var StringLengthCounter = class _StringLengthCounter {
122379
122223
  return counts;
122380
122224
  }
122381
122225
  /**
122382
- * Count .length accesses in a block.
122226
+ * Count .char_count accesses in a block.
122383
122227
  */
122384
122228
  static countBlock(ctx) {
122385
122229
  const counts = /* @__PURE__ */ new Map();
@@ -122389,7 +122233,7 @@ var StringLengthCounter = class _StringLengthCounter {
122389
122233
  return counts;
122390
122234
  }
122391
122235
  /**
122392
- * Count .length accesses in a block, adding to existing counts.
122236
+ * Count .char_count accesses in a block, adding to existing counts.
122393
122237
  */
122394
122238
  static countBlockInto(ctx, counts) {
122395
122239
  for (const stmt of ctx.statement()) {
@@ -122397,7 +122241,7 @@ var StringLengthCounter = class _StringLengthCounter {
122397
122241
  }
122398
122242
  }
122399
122243
  /**
122400
- * Walk an expression tree, counting .length accesses.
122244
+ * Walk an expression tree, counting .char_count accesses.
122401
122245
  * Uses generic traversal - only postfix expressions need special handling.
122402
122246
  */
122403
122247
  static walkExpression(ctx, counts) {
@@ -122472,7 +122316,7 @@ var StringLengthCounter = class _StringLengthCounter {
122472
122316
  }
122473
122317
  }
122474
122318
  /**
122475
- * Walk a postfix expression - this is where we detect .length accesses.
122319
+ * Walk a postfix expression - this is where we detect .char_count accesses.
122476
122320
  */
122477
122321
  static walkPostfixExpr(ctx, counts) {
122478
122322
  const primary = ctx.primaryExpression();
@@ -122481,7 +122325,7 @@ var StringLengthCounter = class _StringLengthCounter {
122481
122325
  if (primaryId && ops.length > 0) {
122482
122326
  for (const op of ops) {
122483
122327
  const memberName = op.IDENTIFIER()?.getText();
122484
- if (memberName === "length") {
122328
+ if (memberName === "char_count") {
122485
122329
  const typeInfo = CodeGenState.getVariableTypeInfo(primaryId);
122486
122330
  if (typeInfo?.isString) {
122487
122331
  const currentCount = counts.get(primaryId) || 0;
@@ -122498,7 +122342,7 @@ var StringLengthCounter = class _StringLengthCounter {
122498
122342
  }
122499
122343
  }
122500
122344
  /**
122501
- * Walk a statement, counting .length accesses.
122345
+ * Walk a statement, counting .char_count accesses.
122502
122346
  */
122503
122347
  static walkStatement(ctx, counts) {
122504
122348
  if (ctx.assignmentStatement()) {
@@ -122530,95 +122374,6 @@ var StringLengthCounter = class _StringLengthCounter {
122530
122374
  };
122531
122375
  var StringLengthCounter_default = StringLengthCounter;
122532
122376
 
122533
- // src/transpiler/output/codegen/helpers/CppModeHelper.ts
122534
- var CppModeHelper = class {
122535
- /**
122536
- * Get address-of expression for struct parameter passing.
122537
- * C mode: `&expr` (pass pointer to struct)
122538
- * C++ mode: `expr` (pass reference directly)
122539
- *
122540
- * @param expr - The expression to potentially wrap
122541
- * @returns The expression with address-of operator in C mode
122542
- */
122543
- static maybeAddressOf(expr) {
122544
- return CodeGenState.cppMode ? expr : `&${expr}`;
122545
- }
122546
- /**
122547
- * Get dereference expression for struct parameter access.
122548
- * C mode: `(*expr)` (dereference pointer)
122549
- * C++ mode: `expr` (reference can be used directly)
122550
- *
122551
- * @param expr - The expression to potentially dereference
122552
- * @returns The expression with dereference in C mode
122553
- */
122554
- static maybeDereference(expr) {
122555
- return CodeGenState.cppMode ? expr : `(*${expr})`;
122556
- }
122557
- /**
122558
- * Get the type modifier for struct parameter declarations.
122559
- * C mode: `*` (pointer type)
122560
- * C++ mode: `&` (reference type)
122561
- *
122562
- * @returns The type modifier character
122563
- */
122564
- static refOrPtr() {
122565
- return CodeGenState.cppMode ? "&" : "*";
122566
- }
122567
- /**
122568
- * Get the member access separator for struct parameters.
122569
- * C mode: `->` (pointer member access)
122570
- * C++ mode: `.` (reference member access)
122571
- *
122572
- * @returns The member access separator
122573
- */
122574
- static memberSeparator() {
122575
- return CodeGenState.cppMode ? "." : "->";
122576
- }
122577
- /**
122578
- * Get NULL literal for the current mode.
122579
- * C mode: `NULL`
122580
- * C++ mode: `nullptr`
122581
- *
122582
- * @returns The null pointer literal
122583
- */
122584
- static nullLiteral() {
122585
- return CodeGenState.cppMode ? "nullptr" : "NULL";
122586
- }
122587
- /**
122588
- * Generate a cast expression for the current mode.
122589
- * C mode: `(type)expr`
122590
- * C++ mode: `static_cast<type>(expr)`
122591
- *
122592
- * @param type - The target type
122593
- * @param expr - The expression to cast
122594
- * @returns The cast expression
122595
- */
122596
- static cast(type, expr) {
122597
- return CodeGenState.cppMode ? `static_cast<${type}>(${expr})` : `(${type})${expr}`;
122598
- }
122599
- /**
122600
- * Generate a reinterpret cast expression for the current mode.
122601
- * C mode: `(type)expr`
122602
- * C++ mode: `reinterpret_cast<type>(expr)`
122603
- *
122604
- * @param type - The target type
122605
- * @param expr - The expression to cast
122606
- * @returns The cast expression
122607
- */
122608
- static reinterpretCast(type, expr) {
122609
- return CodeGenState.cppMode ? `reinterpret_cast<${type}>(${expr})` : `(${type})${expr}`;
122610
- }
122611
- /**
122612
- * Check if we're in C++ mode.
122613
- *
122614
- * @returns True if generating C++ code
122615
- */
122616
- static isCppMode() {
122617
- return CodeGenState.cppMode;
122618
- }
122619
- };
122620
- var CppModeHelper_default = CppModeHelper;
122621
-
122622
122377
  // src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts
122623
122378
  var MemberChainAnalyzer = class _MemberChainAnalyzer {
122624
122379
  /**
@@ -125648,6 +125403,33 @@ var TypedefParamParser = class _TypedefParamParser {
125648
125403
  static shouldBeConst(typedefType, paramIndex) {
125649
125404
  return _TypedefParamParser.getParamAt(typedefType, paramIndex)?.isConst ?? null;
125650
125405
  }
125406
+ /**
125407
+ * Resolve callback pointer/const overrides onto an array of IParameterSymbol.
125408
+ *
125409
+ * Issue #914: This is the single point where callback typedef info is applied
125410
+ * to parameters — used by both .c and .h generation paths via IParameterSymbol.
125411
+ *
125412
+ * @param params - The original parameter symbols
125413
+ * @param callbackTypedefType - The typedef type string (e.g., "void (*)(widget_t *, const rect_t *)")
125414
+ * @returns New array with isCallbackPointer/isCallbackConst resolved
125415
+ */
125416
+ static resolveCallbackParams(params, callbackTypedefType) {
125417
+ return params.map((param, index) => {
125418
+ const shouldBePointer = _TypedefParamParser.shouldBePointer(
125419
+ callbackTypedefType,
125420
+ index
125421
+ );
125422
+ const shouldBeConst = _TypedefParamParser.shouldBeConst(
125423
+ callbackTypedefType,
125424
+ index
125425
+ );
125426
+ return {
125427
+ ...param,
125428
+ isCallbackPointer: shouldBePointer ?? void 0,
125429
+ isCallbackConst: shouldBeConst ?? void 0
125430
+ };
125431
+ });
125432
+ }
125651
125433
  };
125652
125434
  var TypedefParamParser_default = TypedefParamParser;
125653
125435
 
@@ -126419,7 +126201,7 @@ var PassByValueAnalyzer = class _PassByValueAnalyzer {
126419
126201
  * Used by both function call tracking and subscript access tracking.
126420
126202
  */
126421
126203
  static walkOrExpression(orExpr, handler) {
126422
- orExpr.andExpression().flatMap((and) => and.equalityExpression()).flatMap((eq) => eq.relationalExpression()).flatMap((rel) => rel.bitwiseOrExpression()).flatMap((bor) => bor.bitwiseXorExpression()).flatMap((bxor) => bxor.bitwiseAndExpression()).flatMap((band) => band.shiftExpression()).flatMap((shift) => shift.additiveExpression()).flatMap((add) => add.multiplicativeExpression()).flatMap((mul) => mul.unaryExpression()).forEach(handler);
126204
+ ExpressionUtils_default.collectUnaryFromOrExpr(orExpr).forEach(handler);
126423
126205
  }
126424
126206
  /**
126425
126207
  * Walk an orExpression tree for function calls.
@@ -126774,6 +126556,7 @@ var ParameterInputAdapter = class {
126774
126556
  isPassByReference: false
126775
126557
  };
126776
126558
  }
126559
+ const isCallbackPointer = param.isCallbackPointer ?? false;
126777
126560
  return {
126778
126561
  name: param.name,
126779
126562
  baseType: param.type,
@@ -126783,8 +126566,10 @@ var ParameterInputAdapter = class {
126783
126566
  isArray: false,
126784
126567
  isCallback: false,
126785
126568
  isString: false,
126786
- isPassByValue: deps.isPassByValue,
126787
- isPassByReference: !deps.isPassByValue
126569
+ isPassByValue: isCallbackPointer ? false : deps.isPassByValue,
126570
+ isPassByReference: isCallbackPointer ? true : !deps.isPassByValue,
126571
+ forcePointerSyntax: isCallbackPointer || void 0,
126572
+ forceConst: param.isCallbackConst || void 0
126788
126573
  };
126789
126574
  }
126790
126575
  /**
@@ -127594,7 +127379,7 @@ var CodeGenerator = class _CodeGenerator {
127594
127379
  if (!arrayAccessMatch) {
127595
127380
  return false;
127596
127381
  }
127597
- if (text.endsWith(".length") || text.endsWith(".capacity") || text.endsWith(".size")) {
127382
+ if (text.endsWith(".length") || text.endsWith(".capacity") || text.endsWith(".size") || text.endsWith(".bit_length") || text.endsWith(".byte_length") || text.endsWith(".element_count") || text.endsWith(".char_count")) {
127598
127383
  return false;
127599
127384
  }
127600
127385
  const arrayName = arrayAccessMatch[1];
@@ -128654,7 +128439,8 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
128654
128439
  );
128655
128440
  if (symbols.hasPublicSymbols() && CodeGenState.sourcePath) {
128656
128441
  const pathToUse = options?.sourceRelativePath || CodeGenState.sourcePath.replace(/^.*[\\/]/, "");
128657
- const headerName = pathToUse.replace(/\.cnx$|\.cnext$/, ".h");
128442
+ const ext = CodeGenState.cppMode ? ".hpp" : ".h";
128443
+ const headerName = pathToUse.replace(/\.cnx$|\.cnext$/, ext);
128658
128444
  output.push(`#include "${headerName}"`, "");
128659
128445
  CodeGenState.selfIncludeAdded = true;
128660
128446
  }
@@ -131102,10 +130888,15 @@ var HeaderGeneratorUtils = class _HeaderGeneratorUtils {
131102
130888
  }
131103
130889
  if (options.userIncludes && options.userIncludes.length > 0) {
131104
130890
  for (const include of options.userIncludes) {
131105
- lines.push(include);
130891
+ const transformedInclude = options.cppMode ? include.replace(/\.h"/, '.hpp"').replace(/\.h>/, ".hpp>") : include;
130892
+ lines.push(transformedInclude);
131106
130893
  }
131107
130894
  }
131108
- const userIncludeSet = new Set(options.userIncludes ?? []);
130895
+ const userIncludeSet = new Set(
130896
+ options.userIncludes?.map(
130897
+ (inc) => options.cppMode ? inc.replace(/\.h"/, '.hpp"').replace(/\.h>/, ".hpp>") : inc
130898
+ ) ?? []
130899
+ );
131109
130900
  for (const directive of headersToInclude) {
131110
130901
  if (!userIncludeSet.has(directive)) {
131111
130902
  lines.push(directive);
@@ -132919,7 +132710,7 @@ var TSymbolInfoAdapter = class _TSymbolInfoAdapter {
132919
132710
  var TSymbolInfoAdapter_default = TSymbolInfoAdapter;
132920
132711
 
132921
132712
  // src/transpiler/logic/symbols/SymbolUtils.ts
132922
- var RESERVED_FIELD_NAMES = /* @__PURE__ */ new Set(["length"]);
132713
+ var RESERVED_FIELD_NAMES = /* @__PURE__ */ new Set([]);
132923
132714
  function parseArrayDimensions2(text) {
132924
132715
  const dimensions = [];
132925
132716
  const arrayMatches = text.match(/\[(\d+)\]/g);
@@ -138040,6 +137831,162 @@ var ArrayIndexTypeAnalyzer = class {
138040
137831
  };
138041
137832
  var ArrayIndexTypeAnalyzer_default = ArrayIndexTypeAnalyzer;
138042
137833
 
137834
+ // src/transpiler/logic/analysis/SignedShiftAnalyzer.ts
137835
+ import { ParseTreeWalker as ParseTreeWalker9 } from "antlr4ng";
137836
+ var SignedVariableCollector = class extends CNextListener {
137837
+ signedVars = /* @__PURE__ */ new Set();
137838
+ getSignedVars() {
137839
+ return this.signedVars;
137840
+ }
137841
+ /**
137842
+ * Track a typed identifier if it has a signed type
137843
+ */
137844
+ trackIfSigned(typeCtx, identifier) {
137845
+ if (!typeCtx) return;
137846
+ const typeName = typeCtx.getText();
137847
+ if (!TypeConstants_default.SIGNED_TYPES.includes(typeName)) return;
137848
+ if (!identifier) return;
137849
+ this.signedVars.add(identifier.getText());
137850
+ }
137851
+ /**
137852
+ * Track variable declarations with signed types
137853
+ */
137854
+ enterVariableDeclaration = (ctx) => {
137855
+ this.trackIfSigned(ctx.type(), ctx.IDENTIFIER());
137856
+ };
137857
+ /**
137858
+ * Track function parameters with signed types
137859
+ */
137860
+ enterParameter = (ctx) => {
137861
+ this.trackIfSigned(ctx.type(), ctx.IDENTIFIER());
137862
+ };
137863
+ /**
137864
+ * Track for-loop variable declarations with signed types
137865
+ */
137866
+ enterForVarDecl = (ctx) => {
137867
+ this.trackIfSigned(ctx.type(), ctx.IDENTIFIER());
137868
+ };
137869
+ };
137870
+ var SignedShiftListener = class extends CNextListener {
137871
+ analyzer;
137872
+ // eslint-disable-next-line @typescript-eslint/lines-between-class-members
137873
+ signedVars;
137874
+ constructor(analyzer, signedVars) {
137875
+ super();
137876
+ this.analyzer = analyzer;
137877
+ this.signedVars = signedVars;
137878
+ }
137879
+ /**
137880
+ * Check shift expressions for signed operands
137881
+ * shiftExpression: additiveExpression (('<<' | '>>') additiveExpression)*
137882
+ */
137883
+ enterShiftExpression = (ctx) => {
137884
+ const operands = ctx.additiveExpression();
137885
+ if (operands.length < 2) return;
137886
+ for (let i = 0; i < operands.length - 1; i++) {
137887
+ const operatorToken = ctx.getChild(i * 2 + 1);
137888
+ if (!operatorToken) continue;
137889
+ const operator = operatorToken.getText();
137890
+ if (operator !== "<<" && operator !== ">>") continue;
137891
+ const leftOperand = operands[i];
137892
+ if (this.isSignedOperand(leftOperand)) {
137893
+ const { line, column } = ParserUtils_default.getPosition(leftOperand);
137894
+ this.analyzer.addError(line, column, operator);
137895
+ }
137896
+ }
137897
+ };
137898
+ /**
137899
+ * Check if an additive expression contains a signed type operand
137900
+ */
137901
+ isSignedOperand(ctx) {
137902
+ const multExprs = ctx.multiplicativeExpression();
137903
+ for (const multExpr of multExprs) {
137904
+ const unaryExprs = multExpr.unaryExpression();
137905
+ for (const unaryExpr of unaryExprs) {
137906
+ if (this.isSignedUnaryExpression(unaryExpr)) {
137907
+ return true;
137908
+ }
137909
+ }
137910
+ }
137911
+ return false;
137912
+ }
137913
+ /**
137914
+ * Check if a unary expression is a signed type
137915
+ */
137916
+ isSignedUnaryExpression(ctx) {
137917
+ if (ctx.MINUS()) {
137918
+ const nestedUnary = ctx.unaryExpression();
137919
+ if (nestedUnary) {
137920
+ const nestedPostfix = nestedUnary.postfixExpression();
137921
+ if (nestedPostfix) {
137922
+ const nestedPrimary = nestedPostfix.primaryExpression();
137923
+ if (nestedPrimary?.literal()) {
137924
+ return true;
137925
+ }
137926
+ }
137927
+ return this.isSignedUnaryExpression(nestedUnary);
137928
+ }
137929
+ return false;
137930
+ }
137931
+ const postfixExpr = ctx.postfixExpression();
137932
+ if (!postfixExpr) return false;
137933
+ const primaryExpr = postfixExpr.primaryExpression();
137934
+ if (!primaryExpr) return false;
137935
+ const parenExpr = primaryExpr.expression();
137936
+ if (parenExpr) {
137937
+ return this.isSignedExpression(parenExpr);
137938
+ }
137939
+ const identifier = primaryExpr.IDENTIFIER();
137940
+ if (identifier) {
137941
+ return this.signedVars.has(identifier.getText());
137942
+ }
137943
+ return false;
137944
+ }
137945
+ /**
137946
+ * Check if a full expression contains signed operands
137947
+ */
137948
+ isSignedExpression(ctx) {
137949
+ const ternary = ctx.ternaryExpression();
137950
+ if (!ternary) return false;
137951
+ const additiveExprs = ExpressionUtils_default.collectAdditiveExpressions(ternary);
137952
+ return additiveExprs.some((addExpr) => this.isSignedOperand(addExpr));
137953
+ }
137954
+ };
137955
+ var SignedShiftAnalyzer = class {
137956
+ errors = [];
137957
+ /**
137958
+ * Analyze the parse tree for signed shift operations
137959
+ */
137960
+ analyze(tree) {
137961
+ this.errors = [];
137962
+ const collector = new SignedVariableCollector();
137963
+ ParseTreeWalker9.DEFAULT.walk(collector, tree);
137964
+ const signedVars = collector.getSignedVars();
137965
+ const listener = new SignedShiftListener(this, signedVars);
137966
+ ParseTreeWalker9.DEFAULT.walk(listener, tree);
137967
+ return this.errors;
137968
+ }
137969
+ /**
137970
+ * Add a signed shift error
137971
+ */
137972
+ addError(line, column, operator) {
137973
+ this.errors.push({
137974
+ code: "E0805",
137975
+ line,
137976
+ column,
137977
+ message: `Shift operator '${operator}' not allowed on signed integer types`,
137978
+ helpText: "Shift operations on signed integers have undefined (<<) or implementation-defined (>>) behavior. Use unsigned types (u8, u16, u32, u64) for bit manipulation."
137979
+ });
137980
+ }
137981
+ /**
137982
+ * Get all detected errors
137983
+ */
137984
+ getErrors() {
137985
+ return this.errors;
137986
+ }
137987
+ };
137988
+ var SignedShiftAnalyzer_default = SignedShiftAnalyzer;
137989
+
138043
137990
  // src/transpiler/logic/analysis/runAnalyzers.ts
138044
137991
  function collectErrors(analyzerErrors, target, formatMessage) {
138045
137992
  const formatter = formatMessage ?? ((e) => e.message);
@@ -138097,6 +138044,10 @@ function runAnalyzers(tree, tokenStream, options) {
138097
138044
  if (collectErrors(indexTypeAnalyzer.analyze(tree), errors, formatWithCode)) {
138098
138045
  return errors;
138099
138046
  }
138047
+ const signedShiftAnalyzer = new SignedShiftAnalyzer_default();
138048
+ if (collectErrors(signedShiftAnalyzer.analyze(tree), errors, formatWithCode)) {
138049
+ return errors;
138050
+ }
138100
138051
  const commentExtractor = new CommentExtractor_default(tokenStream);
138101
138052
  collectErrors(
138102
138053
  commentExtractor.validate(),
@@ -139192,7 +139143,8 @@ var Transpiler = class {
139192
139143
  const headerContent = this.generateHeaderForFile(file);
139193
139144
  if (headerContent) {
139194
139145
  const headerPath = this.pathResolver.getHeaderOutputPath(
139195
- file.discoveredFile
139146
+ file.discoveredFile,
139147
+ this.cppDetected
139196
139148
  );
139197
139149
  this.fs.writeFile(headerPath, headerContent);
139198
139150
  result.outputFiles.push(headerPath);
@@ -139595,7 +139547,8 @@ var Transpiler = class {
139595
139547
  if (exportedSymbols.length === 0) {
139596
139548
  return null;
139597
139549
  }
139598
- const headerName = basename5(sourcePath).replace(/\.cnx$|\.cnext$/, ".h");
139550
+ const ext = this.cppDetected ? ".hpp" : ".h";
139551
+ const headerName = basename5(sourcePath).replace(/\.cnx$|\.cnext$/, ext);
139599
139552
  const typeInput = this.state.getSymbolInfo(sourcePath);
139600
139553
  const passByValueParams = this.state.getPassByValueParams(sourcePath) ?? /* @__PURE__ */ new Map();
139601
139554
  const userIncludes = this.state.getUserIncludes(sourcePath);
@@ -139665,19 +139618,31 @@ var Transpiler = class {
139665
139618
  convertToHeaderSymbols(symbols, unmodifiedParams, knownEnums) {
139666
139619
  return symbols.map((symbol) => {
139667
139620
  const headerSymbol = HeaderSymbolAdapter_default.fromTSymbol(symbol);
139668
- if (symbol.kind === "function" && headerSymbol.parameters && headerSymbol.parameters.length > 0) {
139669
- const unmodified = unmodifiedParams.get(headerSymbol.name);
139670
- if (unmodified) {
139671
- const updatedParams = headerSymbol.parameters.map((param) => {
139672
- const isPointerParam = !param.isConst && !param.isArray && param.type !== "f32" && param.type !== "f64" && param.type !== "ISR" && !knownEnums.has(param.type ?? "");
139673
- const isArrayParam = param.isArray && !param.isConst;
139674
- if ((isPointerParam || isArrayParam) && unmodified.has(param.name)) {
139675
- return { ...param, isAutoConst: true };
139676
- }
139677
- return param;
139678
- });
139679
- return { ...headerSymbol, parameters: updatedParams };
139680
- }
139621
+ if (symbol.kind !== "function" || !headerSymbol.parameters || headerSymbol.parameters.length === 0) {
139622
+ return headerSymbol;
139623
+ }
139624
+ const typedefName = CodeGenState.callbackCompatibleFunctions.get(
139625
+ headerSymbol.name
139626
+ );
139627
+ const callbackTypedefType = typedefName ? CodeGenState.getTypedefType(typedefName) : void 0;
139628
+ if (callbackTypedefType) {
139629
+ const updatedParams = TypedefParamParser_default.resolveCallbackParams(
139630
+ headerSymbol.parameters,
139631
+ callbackTypedefType
139632
+ );
139633
+ return { ...headerSymbol, parameters: updatedParams };
139634
+ }
139635
+ const unmodified = unmodifiedParams.get(headerSymbol.name);
139636
+ if (unmodified) {
139637
+ const updatedParams = headerSymbol.parameters.map((param) => {
139638
+ const isPointerParam = !param.isConst && !param.isArray && param.type !== "f32" && param.type !== "f64" && param.type !== "ISR" && !knownEnums.has(param.type ?? "");
139639
+ const isArrayParam = param.isArray && !param.isConst;
139640
+ if ((isPointerParam || isArrayParam) && unmodified.has(param.name)) {
139641
+ return { ...param, isAutoConst: true };
139642
+ }
139643
+ return param;
139644
+ });
139645
+ return { ...headerSymbol, parameters: updatedParams };
139681
139646
  }
139682
139647
  return headerSymbol;
139683
139648
  });