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.
- package/dist/index.js +398 -433
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +50 -29
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -1
- package/src/transpiler/data/PathResolver.ts +10 -6
- package/src/transpiler/data/__tests__/PathResolver.test.ts +32 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +1 -12
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +239 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +414 -0
- package/src/transpiler/logic/analysis/__tests__/StructFieldAnalyzer.test.ts +15 -75
- package/src/transpiler/logic/analysis/__tests__/runAnalyzers.test.ts +3 -15
- package/src/transpiler/logic/analysis/runAnalyzers.ts +11 -2
- package/src/transpiler/logic/analysis/types/ISignedShiftError.ts +15 -0
- package/src/transpiler/logic/symbols/SymbolUtils.ts +4 -1
- package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +10 -11
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +4 -4
- package/src/transpiler/logic/symbols/cpp/__tests__/CppResolver.integration.test.ts +4 -4
- package/src/transpiler/output/codegen/CodeGenerator.ts +12 -4
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +26 -26
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +12 -11
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +21 -21
- package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +7 -326
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +20 -0
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +18 -275
- package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +13 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/AccessExprGenerator.test.ts +0 -573
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +60 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +8 -439
- package/src/transpiler/output/codegen/generators/expressions/__tests__/UnaryExprGenerator.test.ts +196 -0
- package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +8 -4
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +7 -2
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +34 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +48 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +88 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +15 -2
- package/src/transpiler/output/headers/__tests__/BaseHeaderGenerator.test.ts +87 -0
- package/src/utils/ExpressionUtils.ts +51 -0
- 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
|
-
|
|
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 (.
|
|
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
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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
|
-
|
|
1389
|
-
|
|
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("~"))
|
|
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)
|