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
@@ -235,6 +235,10 @@ const generatePostfixExpression = (
235
235
  }
236
236
 
237
237
  // ADR-006: If a struct parameter is used as a whole value (no postfix ops)
238
+ // This applies to both normal struct params AND callback-promoted struct params.
239
+ // When used as a value (assignments, etc.), we need to dereference to get the struct.
240
+ // Issue #937: For function arguments expecting pointers, CallExprGenerator handles
241
+ // using the identifier directly instead of the dereferenced form.
238
242
  if (isStructParam && ops.length === 0) {
239
243
  return {
240
244
  code: memberAccessChain.wrapStructParamValue(result, {
@@ -386,7 +390,7 @@ const handleGlobalPrefix = (
386
390
  const handleThisScopeLength = (
387
391
  memberName: string,
388
392
  tracking: ITrackingState,
389
- input: IGeneratorInput,
393
+ _input: IGeneratorInput,
390
394
  state: IGeneratorState,
391
395
  orchestrator: IOrchestrator,
392
396
  ): boolean => {
@@ -531,8 +535,10 @@ const tryExplicitLengthProperty = (
531
535
  };
532
536
 
533
537
  /**
534
- * Try handling property access (.length, .capacity, .size, .bit_length, .byte_length, .element_count, .char_count).
538
+ * Try handling property access (.capacity, .size, .bit_length, .byte_length, .element_count, .char_count).
535
539
  * Returns true if handled.
540
+ *
541
+ * Note: .length was removed in favor of explicit properties (ADR-058).
536
542
  */
537
543
  const tryPropertyAccess = (
538
544
  memberName: string,
@@ -543,20 +549,13 @@ const tryPropertyAccess = (
543
549
  orchestrator: IOrchestrator,
544
550
  effects: TGeneratorEffect[],
545
551
  ): boolean => {
552
+ // ADR-058: .length is deprecated - use explicit properties instead
546
553
  if (memberName === "length") {
547
- const ctx = createExplicitLengthContext(tracking, rootIdentifier);
548
- const lengthResult = generateLengthProperty(
549
- ctx,
550
- input,
551
- state,
552
- orchestrator,
553
- effects,
554
+ throw new Error(
555
+ `Error: '.length' on '${tracking.result}' is deprecated. Use explicit properties: ` +
556
+ `.bit_length (bit width), .byte_length (byte size), ` +
557
+ `.element_count (array size), or .char_count (string length)`,
554
558
  );
555
- if (lengthResult !== null) {
556
- applyPropertyResult(tracking, lengthResult);
557
- return true;
558
- }
559
- return false;
560
559
  }
561
560
 
562
561
  // ADR-058: Explicit length properties
@@ -607,264 +606,6 @@ const tryPropertyAccess = (
607
606
  return false;
608
607
  };
609
608
 
610
- // ========================================================================
611
- // Length Property
612
- // ========================================================================
613
-
614
- /**
615
- * Context for .length property generation.
616
- */
617
- interface ILengthContext {
618
- result: string;
619
- rootIdentifier: string | undefined;
620
- resolvedIdentifier: string | undefined;
621
- previousStructType: string | undefined;
622
- previousMemberName: string | undefined;
623
- subscriptDepth: number;
624
- }
625
-
626
- /**
627
- * Generate .length property access.
628
- * Returns null if not applicable (falls through to member access).
629
- */
630
- const generateLengthProperty = (
631
- ctx: ILengthContext,
632
- input: IGeneratorInput,
633
- state: IGeneratorState,
634
- orchestrator: IOrchestrator,
635
- effects: TGeneratorEffect[],
636
- ): string | null => {
637
- // Special case: main function's args.length -> argc
638
- if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
639
- return "argc";
640
- }
641
-
642
- // Check if we're accessing a struct member (cfg.magic.length)
643
- if (ctx.previousStructType && ctx.previousMemberName) {
644
- const fieldInfo = orchestrator.getStructFieldInfo(
645
- ctx.previousStructType,
646
- ctx.previousMemberName,
647
- );
648
- if (fieldInfo) {
649
- return generateStructFieldLength(
650
- ctx.result,
651
- fieldInfo,
652
- ctx.subscriptDepth,
653
- input,
654
- orchestrator,
655
- effects,
656
- );
657
- }
658
- }
659
-
660
- // Fall back to checking the current resolved identifier's type
661
- const typeInfo = ctx.resolvedIdentifier
662
- ? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
663
- : undefined;
664
-
665
- if (!typeInfo) {
666
- return `/* .length: unknown type for ${ctx.result} */0`;
667
- }
668
-
669
- return generateTypeInfoLength(
670
- ctx.result,
671
- typeInfo,
672
- ctx.subscriptDepth,
673
- ctx.resolvedIdentifier,
674
- input,
675
- state,
676
- effects,
677
- );
678
- };
679
-
680
- /**
681
- * Generate .length for a struct field.
682
- */
683
- const generateStructFieldLength = (
684
- result: string,
685
- fieldInfo: { type: string; dimensions?: (number | string)[] },
686
- subscriptDepth: number,
687
- input: IGeneratorInput,
688
- orchestrator: IOrchestrator,
689
- effects: TGeneratorEffect[],
690
- ): string => {
691
- const memberType = fieldInfo.type;
692
- const dimensions = fieldInfo.dimensions;
693
- const isStringField = TypeCheckUtils.isString(memberType);
694
-
695
- if (dimensions?.length && dimensions.length > 1 && isStringField) {
696
- if (subscriptDepth === 0) {
697
- return String(dimensions[0]);
698
- } else {
699
- effects.push({ type: "include", header: "string" });
700
- return `strlen(${result})`;
701
- }
702
- } else if (dimensions?.length === 1 && isStringField) {
703
- effects.push({ type: "include", header: "string" });
704
- return `strlen(${result})`;
705
- } else if (
706
- dimensions?.length &&
707
- dimensions.length > 0 &&
708
- subscriptDepth < dimensions.length
709
- ) {
710
- return String(dimensions[subscriptDepth]);
711
- } else if (
712
- dimensions?.length &&
713
- dimensions.length > 0 &&
714
- subscriptDepth >= dimensions.length
715
- ) {
716
- return getTypeBitWidth(memberType, input);
717
- } else {
718
- return getTypeBitWidth(memberType, input);
719
- }
720
- };
721
-
722
- /**
723
- * Generate .length from type info.
724
- */
725
- const generateTypeInfoLength = (
726
- result: string,
727
- typeInfo: {
728
- isString?: boolean;
729
- isArray?: boolean;
730
- isEnum?: boolean;
731
- arrayDimensions?: (number | string)[];
732
- baseType: string;
733
- bitWidth?: number;
734
- isBitmap?: boolean;
735
- bitmapTypeName?: string;
736
- },
737
- subscriptDepth: number,
738
- resolvedIdentifier: string | undefined,
739
- input: IGeneratorInput,
740
- state: IGeneratorState,
741
- effects: TGeneratorEffect[],
742
- ): string => {
743
- // ADR-045: String type handling
744
- if (typeInfo.isString) {
745
- return generateStringLength(
746
- result,
747
- typeInfo,
748
- subscriptDepth,
749
- resolvedIdentifier,
750
- state,
751
- );
752
- }
753
-
754
- // Non-string enum - always 32 bits
755
- if (typeInfo.isEnum && !typeInfo.isArray) {
756
- return "32";
757
- }
758
-
759
- // Non-string, non-enum, non-array - use bitWidth
760
- if (!typeInfo.isArray) {
761
- return String(typeInfo.bitWidth || 0);
762
- }
763
-
764
- // Array without dimensions - unknown length
765
- const dims = typeInfo.arrayDimensions;
766
- if (!dims || dims.length === 0) {
767
- return `/* .length unknown for ${resolvedIdentifier} */0`;
768
- }
769
-
770
- // Array with subscript within bounds - return that dimension
771
- if (subscriptDepth < dims.length) {
772
- return String(dims[subscriptDepth]);
773
- }
774
-
775
- // Subscript past array bounds - return element type's length
776
- return generateElementTypeLength(result, typeInfo, input, effects);
777
- };
778
-
779
- /**
780
- * Generate .length for string types.
781
- */
782
- const generateStringLength = (
783
- result: string,
784
- typeInfo: {
785
- arrayDimensions?: (number | string)[];
786
- },
787
- subscriptDepth: number,
788
- resolvedIdentifier: string | undefined,
789
- state: IGeneratorState,
790
- ): string => {
791
- const dims = typeInfo.arrayDimensions;
792
-
793
- // String array (2D): first dimension is array size, second is string capacity
794
- if (dims && dims.length > 1) {
795
- return subscriptDepth === 0 ? String(dims[0]) : `strlen(${result})`;
796
- }
797
-
798
- // String array with subscript - use result which contains arr[index]
799
- // This handles string<32>[5] parameters where subscriptDepth=1 after arr[index]
800
- if (subscriptDepth > 0) {
801
- return `strlen(${result})`;
802
- }
803
-
804
- // Simple string: check length cache first, then use strlen
805
- if (resolvedIdentifier && state.lengthCache?.has(resolvedIdentifier)) {
806
- return state.lengthCache.get(resolvedIdentifier)!;
807
- }
808
- return resolvedIdentifier
809
- ? `strlen(${resolvedIdentifier})`
810
- : `strlen(${result})`;
811
- };
812
-
813
- /**
814
- * Generate .length for array element types (subscript past bounds).
815
- */
816
- const generateElementTypeLength = (
817
- result: string,
818
- typeInfo: {
819
- isEnum?: boolean;
820
- isString?: boolean;
821
- baseType: string;
822
- isBitmap?: boolean;
823
- bitmapTypeName?: string;
824
- },
825
- input: IGeneratorInput,
826
- effects: TGeneratorEffect[],
827
- ): string => {
828
- // Enum element
829
- if (typeInfo.isEnum) {
830
- return "32";
831
- }
832
-
833
- // String element
834
- if (TypeCheckUtils.isString(typeInfo.baseType) || typeInfo.isString) {
835
- effects.push({ type: "include", header: "string" });
836
- return `strlen(${result})`;
837
- }
838
-
839
- // Numeric/bitmap element - get bit width
840
- let elementBitWidth = TYPE_WIDTH[typeInfo.baseType] || 0;
841
- if (elementBitWidth === 0 && typeInfo.isBitmap && typeInfo.bitmapTypeName) {
842
- elementBitWidth =
843
- input.symbols!.bitmapBitWidth.get(typeInfo.bitmapTypeName) || 0;
844
- }
845
-
846
- if (elementBitWidth > 0) {
847
- return String(elementBitWidth);
848
- }
849
- return `/* .length: unsupported element type ${typeInfo.baseType} */0`;
850
- };
851
-
852
- /**
853
- * Get bit width for a type.
854
- */
855
- const getTypeBitWidth = (typeName: string, input: IGeneratorInput): string => {
856
- let bitWidth = TYPE_WIDTH[typeName] || C_TYPE_WIDTH[typeName] || 0;
857
- if (bitWidth === 0 && input.symbolTable) {
858
- const enumWidth = input.symbolTable.getEnumBitWidth(typeName);
859
- if (enumWidth) bitWidth = enumWidth;
860
- }
861
- if (bitWidth > 0) {
862
- return String(bitWidth);
863
- } else {
864
- return `/* .length: unsupported type ${typeName} */0`;
865
- }
866
- };
867
-
868
609
  // ========================================================================
869
610
  // ADR-058: Explicit Length Properties
870
611
  // ========================================================================
@@ -1377,16 +1118,18 @@ const generateCharCountProperty = (
1377
1118
 
1378
1119
  effects.push({ type: "include", header: "string" });
1379
1120
 
1380
- // Check length cache first
1121
+ // Check length cache first (only for simple variable access, not indexed)
1381
1122
  if (
1123
+ ctx.subscriptDepth === 0 &&
1382
1124
  ctx.resolvedIdentifier &&
1383
1125
  state.lengthCache?.has(ctx.resolvedIdentifier)
1384
1126
  ) {
1385
1127
  return state.lengthCache.get(ctx.resolvedIdentifier)!;
1386
1128
  }
1387
1129
 
1388
- const target = ctx.resolvedIdentifier ?? ctx.result;
1389
- return `strlen(${target})`;
1130
+ // Use ctx.result which contains the full expression including any subscripts
1131
+ // e.g., for arr[0].char_count, ctx.result is "arr[0]" not "arr"
1132
+ return `strlen(${ctx.result})`;
1390
1133
  };
1391
1134
 
1392
1135
  // ========================================================================
@@ -11,6 +11,9 @@ import IGeneratorOutput from "../IGeneratorOutput";
11
11
  import IGeneratorInput from "../IGeneratorInput";
12
12
  import IGeneratorState from "../IGeneratorState";
13
13
  import IOrchestrator from "../IOrchestrator";
14
+ import TypeResolver from "../../TypeResolver";
15
+ import TYPE_MAP from "../../types/TYPE_MAP";
16
+ import CppModeHelper from "../../helpers/CppModeHelper";
14
17
 
15
18
  /**
16
19
  * Generate C code for a unary expression.
@@ -43,7 +46,16 @@ const generateUnaryExpr = (
43
46
  // Determine the operator and generate output
44
47
  if (text.startsWith("!")) return { code: `!${inner}`, effects: [] };
45
48
  if (text.startsWith("-")) return { code: `-${inner}`, effects: [] };
46
- if (text.startsWith("~")) return { code: `~${inner}`, effects: [] };
49
+ if (text.startsWith("~")) {
50
+ const innerType = TypeResolver.getUnaryExpressionType(
51
+ node.unaryExpression()!,
52
+ );
53
+ if (innerType && TypeResolver.isUnsignedType(innerType)) {
54
+ const cType = TYPE_MAP[innerType] ?? innerType;
55
+ return { code: CppModeHelper.cast(cType, `~${inner}`), effects: [] };
56
+ }
57
+ return { code: `~${inner}`, effects: [] };
58
+ }
47
59
  if (text.startsWith("&")) return { code: `&${inner}`, effects: [] };
48
60
 
49
61
  // Fallback (shouldn't happen with valid grammar)