c-next 0.1.64 → 0.1.66
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/grammar/CNext.g4 +9 -2
- package/package.json +5 -1
- package/src/transpiler/logic/parser/grammar/CNext.interp +2 -1
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +992 -870
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/FunctionCollector.test.ts +6 -6
- package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolAdapter.test.ts +179 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/VariableCollector.test.ts +55 -0
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolAdapter.ts +76 -8
- package/src/transpiler/logic/symbols/cnext/collectors/FunctionCollector.ts +9 -10
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +7 -1
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +33 -14
- package/src/transpiler/output/codegen/CodeGenerator.ts +243 -166
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +1086 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +254 -22
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +17 -9
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +5 -3
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +12 -7
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +624 -12
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +819 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +337 -0
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +135 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +4 -0
- package/src/transpiler/output/codegen/helpers/VariableDeclarationFormatter.ts +118 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +426 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +315 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclarationFormatter.test.ts +333 -0
- package/src/transpiler/output/codegen/types/IParameterInput.ts +58 -0
- package/src/transpiler/output/codegen/types/IVariableFormatInput.ts +51 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +20 -35
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +21 -48
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +0 -64
|
@@ -451,7 +451,78 @@ const resolveStringTypeInfo = (
|
|
|
451
451
|
};
|
|
452
452
|
|
|
453
453
|
/**
|
|
454
|
-
*
|
|
454
|
+
* Create context object for explicit length property generators.
|
|
455
|
+
*/
|
|
456
|
+
const createExplicitLengthContext = (
|
|
457
|
+
tracking: ITrackingState,
|
|
458
|
+
rootIdentifier: string | undefined,
|
|
459
|
+
): IExplicitLengthContext => ({
|
|
460
|
+
result: tracking.result,
|
|
461
|
+
rootIdentifier,
|
|
462
|
+
resolvedIdentifier: tracking.resolvedIdentifier,
|
|
463
|
+
previousStructType: tracking.previousStructType,
|
|
464
|
+
previousMemberName: tracking.previousMemberName,
|
|
465
|
+
subscriptDepth: tracking.subscriptDepth,
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Apply property result to tracking state.
|
|
470
|
+
*/
|
|
471
|
+
const applyPropertyResult = (
|
|
472
|
+
tracking: ITrackingState,
|
|
473
|
+
result: string,
|
|
474
|
+
): void => {
|
|
475
|
+
tracking.result = result;
|
|
476
|
+
tracking.previousStructType = undefined;
|
|
477
|
+
tracking.previousMemberName = undefined;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Try handling explicit length property (ADR-058).
|
|
482
|
+
* Returns true if handled.
|
|
483
|
+
*/
|
|
484
|
+
const tryExplicitLengthProperty = (
|
|
485
|
+
memberName: string,
|
|
486
|
+
tracking: ITrackingState,
|
|
487
|
+
rootIdentifier: string | undefined,
|
|
488
|
+
input: IGeneratorInput,
|
|
489
|
+
state: IGeneratorState,
|
|
490
|
+
orchestrator: IOrchestrator,
|
|
491
|
+
effects: TGeneratorEffect[],
|
|
492
|
+
): boolean => {
|
|
493
|
+
const ctx = createExplicitLengthContext(tracking, rootIdentifier);
|
|
494
|
+
|
|
495
|
+
let result: string | null = null;
|
|
496
|
+
switch (memberName) {
|
|
497
|
+
case "bit_length":
|
|
498
|
+
result = generateBitLengthProperty(ctx, input, state, orchestrator);
|
|
499
|
+
break;
|
|
500
|
+
case "byte_length":
|
|
501
|
+
result = generateByteLengthProperty(ctx, input, state, orchestrator);
|
|
502
|
+
break;
|
|
503
|
+
case "element_count":
|
|
504
|
+
result = generateElementCountProperty(ctx, input, state, orchestrator);
|
|
505
|
+
break;
|
|
506
|
+
case "char_count":
|
|
507
|
+
result = generateCharCountProperty(
|
|
508
|
+
ctx,
|
|
509
|
+
input,
|
|
510
|
+
state,
|
|
511
|
+
orchestrator,
|
|
512
|
+
effects,
|
|
513
|
+
);
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (result !== null) {
|
|
518
|
+
applyPropertyResult(tracking, result);
|
|
519
|
+
return true;
|
|
520
|
+
}
|
|
521
|
+
return false;
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Try handling property access (.length, .capacity, .size, .bit_length, .byte_length, .element_count, .char_count).
|
|
455
526
|
* Returns true if handled.
|
|
456
527
|
*/
|
|
457
528
|
const tryPropertyAccess = (
|
|
@@ -464,29 +535,40 @@ const tryPropertyAccess = (
|
|
|
464
535
|
effects: TGeneratorEffect[],
|
|
465
536
|
): boolean => {
|
|
466
537
|
if (memberName === "length") {
|
|
538
|
+
const ctx = createExplicitLengthContext(tracking, rootIdentifier);
|
|
467
539
|
const lengthResult = generateLengthProperty(
|
|
468
|
-
|
|
469
|
-
result: tracking.result,
|
|
470
|
-
rootIdentifier,
|
|
471
|
-
resolvedIdentifier: tracking.resolvedIdentifier,
|
|
472
|
-
previousStructType: tracking.previousStructType,
|
|
473
|
-
previousMemberName: tracking.previousMemberName,
|
|
474
|
-
subscriptDepth: tracking.subscriptDepth,
|
|
475
|
-
},
|
|
540
|
+
ctx,
|
|
476
541
|
input,
|
|
477
542
|
state,
|
|
478
543
|
orchestrator,
|
|
479
544
|
effects,
|
|
480
545
|
);
|
|
481
546
|
if (lengthResult !== null) {
|
|
482
|
-
tracking
|
|
483
|
-
tracking.previousStructType = undefined;
|
|
484
|
-
tracking.previousMemberName = undefined;
|
|
547
|
+
applyPropertyResult(tracking, lengthResult);
|
|
485
548
|
return true;
|
|
486
549
|
}
|
|
487
550
|
return false;
|
|
488
551
|
}
|
|
489
552
|
|
|
553
|
+
// ADR-058: Explicit length properties
|
|
554
|
+
const explicitProps = new Set([
|
|
555
|
+
"bit_length",
|
|
556
|
+
"byte_length",
|
|
557
|
+
"element_count",
|
|
558
|
+
"char_count",
|
|
559
|
+
]);
|
|
560
|
+
if (explicitProps.has(memberName)) {
|
|
561
|
+
return tryExplicitLengthProperty(
|
|
562
|
+
memberName,
|
|
563
|
+
tracking,
|
|
564
|
+
rootIdentifier,
|
|
565
|
+
input,
|
|
566
|
+
state,
|
|
567
|
+
orchestrator,
|
|
568
|
+
effects,
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
490
572
|
if (memberName === "capacity") {
|
|
491
573
|
const typeInfo = resolveStringTypeInfo(
|
|
492
574
|
tracking,
|
|
@@ -704,6 +786,12 @@ const generateStringLength = (
|
|
|
704
786
|
return subscriptDepth === 0 ? String(dims[0]) : `strlen(${result})`;
|
|
705
787
|
}
|
|
706
788
|
|
|
789
|
+
// String array with subscript - use result which contains arr[index]
|
|
790
|
+
// This handles string<32>[5] parameters where subscriptDepth=1 after arr[index]
|
|
791
|
+
if (subscriptDepth > 0) {
|
|
792
|
+
return `strlen(${result})`;
|
|
793
|
+
}
|
|
794
|
+
|
|
707
795
|
// Simple string: check length cache first, then use strlen
|
|
708
796
|
if (resolvedIdentifier && state.lengthCache?.has(resolvedIdentifier)) {
|
|
709
797
|
return state.lengthCache.get(resolvedIdentifier)!;
|
|
@@ -768,6 +856,530 @@ const getTypeBitWidth = (typeName: string, input: IGeneratorInput): string => {
|
|
|
768
856
|
}
|
|
769
857
|
};
|
|
770
858
|
|
|
859
|
+
// ========================================================================
|
|
860
|
+
// ADR-058: Explicit Length Properties
|
|
861
|
+
// ========================================================================
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Context for explicit length property generation.
|
|
865
|
+
*/
|
|
866
|
+
interface IExplicitLengthContext {
|
|
867
|
+
result: string;
|
|
868
|
+
rootIdentifier: string | undefined;
|
|
869
|
+
resolvedIdentifier: string | undefined;
|
|
870
|
+
previousStructType: string | undefined;
|
|
871
|
+
previousMemberName: string | undefined;
|
|
872
|
+
subscriptDepth: number;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Get the numeric bit width for a type (internal helper for ADR-058).
|
|
877
|
+
* Returns 0 if type is unknown.
|
|
878
|
+
*
|
|
879
|
+
* Note: This differs from getTypeBitWidth() which returns a string and is
|
|
880
|
+
* used for the legacy .length property. This function returns a number for
|
|
881
|
+
* use in calculations (e.g., array total bits = elements * element width).
|
|
882
|
+
*/
|
|
883
|
+
const getNumericBitWidth = (
|
|
884
|
+
typeName: string,
|
|
885
|
+
input: IGeneratorInput,
|
|
886
|
+
): number => {
|
|
887
|
+
let bitWidth = TYPE_WIDTH[typeName] ?? C_TYPE_WIDTH[typeName] ?? 0;
|
|
888
|
+
if (bitWidth === 0 && input.symbolTable) {
|
|
889
|
+
const enumWidth = input.symbolTable.getEnumBitWidth(typeName);
|
|
890
|
+
if (enumWidth) bitWidth = enumWidth;
|
|
891
|
+
}
|
|
892
|
+
// Check if it's a known enum (default to 32 bits per ADR-017)
|
|
893
|
+
if (bitWidth === 0 && input.symbols?.knownEnums?.has(typeName)) {
|
|
894
|
+
bitWidth = 32;
|
|
895
|
+
}
|
|
896
|
+
// Check bitmap types
|
|
897
|
+
if (bitWidth === 0 && input.symbols?.bitmapBitWidth) {
|
|
898
|
+
const bitmapWidth = input.symbols.bitmapBitWidth.get(typeName);
|
|
899
|
+
if (bitmapWidth) bitWidth = bitmapWidth;
|
|
900
|
+
}
|
|
901
|
+
return bitWidth;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Generate .bit_length property access (ADR-058).
|
|
906
|
+
* Returns the bit width of any type.
|
|
907
|
+
*/
|
|
908
|
+
const generateBitLengthProperty = (
|
|
909
|
+
ctx: IExplicitLengthContext,
|
|
910
|
+
input: IGeneratorInput,
|
|
911
|
+
state: IGeneratorState,
|
|
912
|
+
orchestrator: IOrchestrator,
|
|
913
|
+
): string | null => {
|
|
914
|
+
// Special case: main function's args.bit_length -> not supported
|
|
915
|
+
if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
`Error: .bit_length is not supported on 'args' parameter. Use .element_count for argc.`,
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Check struct member access
|
|
922
|
+
if (ctx.previousStructType && ctx.previousMemberName) {
|
|
923
|
+
const fieldInfo = orchestrator.getStructFieldInfo(
|
|
924
|
+
ctx.previousStructType,
|
|
925
|
+
ctx.previousMemberName,
|
|
926
|
+
);
|
|
927
|
+
if (fieldInfo) {
|
|
928
|
+
return generateStructFieldBitLength(fieldInfo, ctx.subscriptDepth, input);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Get type info for the resolved identifier
|
|
933
|
+
const typeInfo = ctx.resolvedIdentifier
|
|
934
|
+
? input.typeRegistry.get(ctx.resolvedIdentifier)
|
|
935
|
+
: undefined;
|
|
936
|
+
|
|
937
|
+
if (!typeInfo) {
|
|
938
|
+
throw new Error(
|
|
939
|
+
`Error: Cannot determine .bit_length for '${ctx.result}' - type not found in registry.`,
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
return generateTypeInfoBitLength(typeInfo, ctx.subscriptDepth, input);
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Calculate product of remaining array dimensions from subscript depth.
|
|
948
|
+
* Returns null if a dynamic dimension (C macro) is encountered.
|
|
949
|
+
*/
|
|
950
|
+
const calculateRemainingDimensionsProduct = (
|
|
951
|
+
dimensions: (number | string)[],
|
|
952
|
+
subscriptDepth: number,
|
|
953
|
+
): { product: number } | { dynamicDim: string } => {
|
|
954
|
+
let product = 1;
|
|
955
|
+
for (let i = subscriptDepth; i < dimensions.length; i++) {
|
|
956
|
+
const dim = dimensions[i];
|
|
957
|
+
if (typeof dim !== "number") {
|
|
958
|
+
return { dynamicDim: dim };
|
|
959
|
+
}
|
|
960
|
+
product *= dim;
|
|
961
|
+
}
|
|
962
|
+
return { product };
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Generate bit length for string type from type name.
|
|
967
|
+
*/
|
|
968
|
+
const generateStringBitLengthFromTypeName = (
|
|
969
|
+
typeName: string,
|
|
970
|
+
): string | null => {
|
|
971
|
+
if (!typeName.startsWith("string<")) {
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
const capacityMatch = /^string<(\d+)>$/.exec(typeName);
|
|
975
|
+
if (capacityMatch) {
|
|
976
|
+
const capacity = Number(capacityMatch[1]);
|
|
977
|
+
return String((capacity + 1) * 8);
|
|
978
|
+
}
|
|
979
|
+
return null;
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Generate .bit_length for a struct field.
|
|
984
|
+
*/
|
|
985
|
+
const generateStructFieldBitLength = (
|
|
986
|
+
fieldInfo: { type: string; dimensions?: (number | string)[] },
|
|
987
|
+
subscriptDepth: number,
|
|
988
|
+
input: IGeneratorInput,
|
|
989
|
+
): string => {
|
|
990
|
+
const memberType = fieldInfo.type;
|
|
991
|
+
const dimensions = fieldInfo.dimensions;
|
|
992
|
+
|
|
993
|
+
// String field: bit_length = (capacity + 1) * 8
|
|
994
|
+
const stringBitLength = generateStringBitLengthFromTypeName(memberType);
|
|
995
|
+
if (stringBitLength !== null) {
|
|
996
|
+
return stringBitLength;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Array field: total bits = product of dimensions * element bit width
|
|
1000
|
+
if (dimensions && dimensions.length > subscriptDepth) {
|
|
1001
|
+
const elementBitWidth = getNumericBitWidth(memberType, input);
|
|
1002
|
+
if (elementBitWidth > 0) {
|
|
1003
|
+
const dimResult = calculateRemainingDimensionsProduct(
|
|
1004
|
+
dimensions,
|
|
1005
|
+
subscriptDepth,
|
|
1006
|
+
);
|
|
1007
|
+
if ("dynamicDim" in dimResult) {
|
|
1008
|
+
return `/* .bit_length: dynamic dimension ${dimResult.dynamicDim} */0`;
|
|
1009
|
+
}
|
|
1010
|
+
return String(dimResult.product * elementBitWidth);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// Scalar or fully subscripted: return element bit width
|
|
1015
|
+
const bitWidth = getNumericBitWidth(memberType, input);
|
|
1016
|
+
if (bitWidth > 0) {
|
|
1017
|
+
return String(bitWidth);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
throw new Error(
|
|
1021
|
+
`Error: Cannot determine .bit_length for unsupported type '${memberType}'.`,
|
|
1022
|
+
);
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Generate bit length for a scalar (non-array) type.
|
|
1027
|
+
*/
|
|
1028
|
+
const generateScalarBitLength = (
|
|
1029
|
+
typeInfo: {
|
|
1030
|
+
isEnum?: boolean;
|
|
1031
|
+
baseType: string;
|
|
1032
|
+
bitWidth?: number;
|
|
1033
|
+
},
|
|
1034
|
+
input: IGeneratorInput,
|
|
1035
|
+
): string => {
|
|
1036
|
+
// Enum type: always 32 bits
|
|
1037
|
+
if (typeInfo.isEnum) {
|
|
1038
|
+
return "32";
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (typeInfo.bitWidth) {
|
|
1042
|
+
return String(typeInfo.bitWidth);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// Try lookup by base type
|
|
1046
|
+
const bitWidth = getNumericBitWidth(typeInfo.baseType, input);
|
|
1047
|
+
if (bitWidth > 0) {
|
|
1048
|
+
return String(bitWidth);
|
|
1049
|
+
}
|
|
1050
|
+
throw new Error(
|
|
1051
|
+
`Error: Cannot determine .bit_length for unsupported type '${typeInfo.baseType}'.`,
|
|
1052
|
+
);
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Get element bit width for array type.
|
|
1057
|
+
*/
|
|
1058
|
+
const getArrayElementBitWidth = (
|
|
1059
|
+
typeInfo: {
|
|
1060
|
+
isEnum?: boolean;
|
|
1061
|
+
baseType: string;
|
|
1062
|
+
bitWidth?: number;
|
|
1063
|
+
},
|
|
1064
|
+
input: IGeneratorInput,
|
|
1065
|
+
): number => {
|
|
1066
|
+
let elementBitWidth = typeInfo.bitWidth || 0;
|
|
1067
|
+
if (elementBitWidth === 0) {
|
|
1068
|
+
elementBitWidth = getNumericBitWidth(typeInfo.baseType, input);
|
|
1069
|
+
}
|
|
1070
|
+
if (elementBitWidth === 0 && typeInfo.isEnum) {
|
|
1071
|
+
elementBitWidth = 32;
|
|
1072
|
+
}
|
|
1073
|
+
return elementBitWidth;
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Generate bit length for an array type.
|
|
1078
|
+
*/
|
|
1079
|
+
const generateArrayBitLength = (
|
|
1080
|
+
typeInfo: {
|
|
1081
|
+
isEnum?: boolean;
|
|
1082
|
+
arrayDimensions?: (number | string)[];
|
|
1083
|
+
baseType: string;
|
|
1084
|
+
bitWidth?: number;
|
|
1085
|
+
},
|
|
1086
|
+
subscriptDepth: number,
|
|
1087
|
+
input: IGeneratorInput,
|
|
1088
|
+
): string => {
|
|
1089
|
+
const dims = typeInfo.arrayDimensions;
|
|
1090
|
+
if (!dims || dims.length === 0) {
|
|
1091
|
+
throw new Error(
|
|
1092
|
+
`Error: Cannot determine .bit_length for array with unknown dimensions.`,
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
const elementBitWidth = getArrayElementBitWidth(typeInfo, input);
|
|
1097
|
+
if (elementBitWidth === 0) {
|
|
1098
|
+
throw new Error(
|
|
1099
|
+
`Error: Cannot determine .bit_length for array with unsupported element type '${typeInfo.baseType}'.`,
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const dimResult = calculateRemainingDimensionsProduct(dims, subscriptDepth);
|
|
1104
|
+
if ("dynamicDim" in dimResult) {
|
|
1105
|
+
return `/* .bit_length: dynamic dimension ${dimResult.dynamicDim} */0`;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
return String(dimResult.product * elementBitWidth);
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Generate .bit_length from type info.
|
|
1113
|
+
*/
|
|
1114
|
+
const generateTypeInfoBitLength = (
|
|
1115
|
+
typeInfo: {
|
|
1116
|
+
isString?: boolean;
|
|
1117
|
+
isArray?: boolean;
|
|
1118
|
+
isEnum?: boolean;
|
|
1119
|
+
arrayDimensions?: (number | string)[];
|
|
1120
|
+
baseType: string;
|
|
1121
|
+
bitWidth?: number;
|
|
1122
|
+
isBitmap?: boolean;
|
|
1123
|
+
bitmapTypeName?: string;
|
|
1124
|
+
stringCapacity?: number;
|
|
1125
|
+
},
|
|
1126
|
+
subscriptDepth: number,
|
|
1127
|
+
input: IGeneratorInput,
|
|
1128
|
+
): string => {
|
|
1129
|
+
// String type: bit_length = (capacity + 1) * 8 (buffer size in bits)
|
|
1130
|
+
if (typeInfo.isString) {
|
|
1131
|
+
if (typeInfo.stringCapacity !== undefined) {
|
|
1132
|
+
return String((typeInfo.stringCapacity + 1) * 8);
|
|
1133
|
+
}
|
|
1134
|
+
throw new Error(
|
|
1135
|
+
`Error: Cannot determine .bit_length for string with unknown capacity.`,
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// Non-array scalar: return bit width
|
|
1140
|
+
if (!typeInfo.isArray) {
|
|
1141
|
+
return generateScalarBitLength(typeInfo, input);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// Array: calculate total bits
|
|
1145
|
+
return generateArrayBitLength(typeInfo, subscriptDepth, input);
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Generate .byte_length property access (ADR-058).
|
|
1150
|
+
* Returns the byte size of any type (bit_length / 8).
|
|
1151
|
+
*/
|
|
1152
|
+
const generateByteLengthProperty = (
|
|
1153
|
+
ctx: IExplicitLengthContext,
|
|
1154
|
+
input: IGeneratorInput,
|
|
1155
|
+
state: IGeneratorState,
|
|
1156
|
+
orchestrator: IOrchestrator,
|
|
1157
|
+
): string | null => {
|
|
1158
|
+
// Special case: main function's args
|
|
1159
|
+
if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
|
|
1160
|
+
throw new Error(
|
|
1161
|
+
`Error: .byte_length is not supported on 'args' parameter. Use .element_count for argc.`,
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// Check struct member access
|
|
1166
|
+
if (ctx.previousStructType && ctx.previousMemberName) {
|
|
1167
|
+
const fieldInfo = orchestrator.getStructFieldInfo(
|
|
1168
|
+
ctx.previousStructType,
|
|
1169
|
+
ctx.previousMemberName,
|
|
1170
|
+
);
|
|
1171
|
+
if (fieldInfo) {
|
|
1172
|
+
const bitLength = generateStructFieldBitLength(
|
|
1173
|
+
fieldInfo,
|
|
1174
|
+
ctx.subscriptDepth,
|
|
1175
|
+
input,
|
|
1176
|
+
);
|
|
1177
|
+
// Parse and convert to bytes
|
|
1178
|
+
const bitValue = Number.parseInt(bitLength, 10);
|
|
1179
|
+
if (!Number.isNaN(bitValue)) {
|
|
1180
|
+
return String(bitValue / 8);
|
|
1181
|
+
}
|
|
1182
|
+
return bitLength.replace(".bit_length", ".byte_length");
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// Get type info for the resolved identifier
|
|
1187
|
+
const typeInfo = ctx.resolvedIdentifier
|
|
1188
|
+
? input.typeRegistry.get(ctx.resolvedIdentifier)
|
|
1189
|
+
: undefined;
|
|
1190
|
+
|
|
1191
|
+
if (!typeInfo) {
|
|
1192
|
+
throw new Error(
|
|
1193
|
+
`Error: Cannot determine .byte_length for '${ctx.result}' - type not found in registry.`,
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const bitLength = generateTypeInfoBitLength(
|
|
1198
|
+
typeInfo,
|
|
1199
|
+
ctx.subscriptDepth,
|
|
1200
|
+
input,
|
|
1201
|
+
);
|
|
1202
|
+
const bitValue = Number.parseInt(bitLength, 10);
|
|
1203
|
+
if (!Number.isNaN(bitValue)) {
|
|
1204
|
+
return String(bitValue / 8);
|
|
1205
|
+
}
|
|
1206
|
+
return bitLength.replace(".bit_length", ".byte_length");
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Get dimension value at subscript depth as string.
|
|
1211
|
+
*/
|
|
1212
|
+
const getDimensionAtDepth = (
|
|
1213
|
+
dimensions: (number | string)[],
|
|
1214
|
+
subscriptDepth: number,
|
|
1215
|
+
): string => {
|
|
1216
|
+
const dim = dimensions[subscriptDepth];
|
|
1217
|
+
return typeof dim === "number" ? String(dim) : dim;
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Generate element_count for struct field.
|
|
1222
|
+
*/
|
|
1223
|
+
const generateStructFieldElementCount = (
|
|
1224
|
+
ctx: IExplicitLengthContext,
|
|
1225
|
+
orchestrator: IOrchestrator,
|
|
1226
|
+
): string | null => {
|
|
1227
|
+
if (!ctx.previousStructType || !ctx.previousMemberName) {
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
const fieldInfo = orchestrator.getStructFieldInfo(
|
|
1232
|
+
ctx.previousStructType,
|
|
1233
|
+
ctx.previousMemberName,
|
|
1234
|
+
);
|
|
1235
|
+
|
|
1236
|
+
if (
|
|
1237
|
+
fieldInfo?.dimensions &&
|
|
1238
|
+
fieldInfo.dimensions.length > ctx.subscriptDepth
|
|
1239
|
+
) {
|
|
1240
|
+
return getDimensionAtDepth(fieldInfo.dimensions, ctx.subscriptDepth);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Non-array field - element_count not applicable
|
|
1244
|
+
throw new Error(
|
|
1245
|
+
`Error: .element_count is only available on arrays, not on '${fieldInfo?.type || ctx.previousMemberName}'.`,
|
|
1246
|
+
);
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Generate element_count from type info.
|
|
1251
|
+
*/
|
|
1252
|
+
const generateTypeInfoElementCount = (
|
|
1253
|
+
ctx: IExplicitLengthContext,
|
|
1254
|
+
input: IGeneratorInput,
|
|
1255
|
+
): string => {
|
|
1256
|
+
const typeInfo = ctx.resolvedIdentifier
|
|
1257
|
+
? input.typeRegistry.get(ctx.resolvedIdentifier)
|
|
1258
|
+
: undefined;
|
|
1259
|
+
|
|
1260
|
+
if (!typeInfo) {
|
|
1261
|
+
throw new Error(
|
|
1262
|
+
`Error: Cannot determine .element_count for '${ctx.result}' - type not found in registry.`,
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
if (!typeInfo.isArray) {
|
|
1267
|
+
throw new Error(
|
|
1268
|
+
`Error: .element_count is only available on arrays, not on '${typeInfo.baseType}'.`,
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
const dims = typeInfo.arrayDimensions;
|
|
1273
|
+
if (!dims || dims.length === 0) {
|
|
1274
|
+
throw new Error(
|
|
1275
|
+
`Error: Cannot determine .element_count for array with unknown dimensions.`,
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (ctx.subscriptDepth < dims.length) {
|
|
1280
|
+
return getDimensionAtDepth(dims, ctx.subscriptDepth);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
throw new Error(
|
|
1284
|
+
`Error: .element_count is not available on array elements. Array is fully subscripted.`,
|
|
1285
|
+
);
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Generate .element_count property access (ADR-058).
|
|
1290
|
+
* Returns element count for arrays or argc for args.
|
|
1291
|
+
*/
|
|
1292
|
+
const generateElementCountProperty = (
|
|
1293
|
+
ctx: IExplicitLengthContext,
|
|
1294
|
+
input: IGeneratorInput,
|
|
1295
|
+
state: IGeneratorState,
|
|
1296
|
+
orchestrator: IOrchestrator,
|
|
1297
|
+
): string | null => {
|
|
1298
|
+
// Special case: main function's args.element_count -> argc
|
|
1299
|
+
if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
|
|
1300
|
+
return "argc";
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Check struct member access for array fields
|
|
1304
|
+
const structResult = generateStructFieldElementCount(ctx, orchestrator);
|
|
1305
|
+
if (structResult !== null) {
|
|
1306
|
+
return structResult;
|
|
1307
|
+
}
|
|
1308
|
+
if (ctx.previousStructType) {
|
|
1309
|
+
// generateStructFieldElementCount threw or returned null but struct context existed
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Get type info for variable
|
|
1314
|
+
return generateTypeInfoElementCount(ctx, input);
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Generate .char_count property access (ADR-058).
|
|
1319
|
+
* Returns strlen() for strings.
|
|
1320
|
+
*/
|
|
1321
|
+
const generateCharCountProperty = (
|
|
1322
|
+
ctx: IExplicitLengthContext,
|
|
1323
|
+
input: IGeneratorInput,
|
|
1324
|
+
state: IGeneratorState,
|
|
1325
|
+
orchestrator: IOrchestrator,
|
|
1326
|
+
effects: TGeneratorEffect[],
|
|
1327
|
+
): string | null => {
|
|
1328
|
+
// Special case: main function's args
|
|
1329
|
+
if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
|
|
1330
|
+
throw new Error(
|
|
1331
|
+
`Error: .char_count is only available on strings, not on 'args'. Use .element_count for argc.`,
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// Check struct member access for string fields
|
|
1336
|
+
if (ctx.previousStructType && ctx.previousMemberName) {
|
|
1337
|
+
const fieldInfo = orchestrator.getStructFieldInfo(
|
|
1338
|
+
ctx.previousStructType,
|
|
1339
|
+
ctx.previousMemberName,
|
|
1340
|
+
);
|
|
1341
|
+
if (fieldInfo?.type.startsWith("string<")) {
|
|
1342
|
+
effects.push({ type: "include", header: "string" });
|
|
1343
|
+
return `strlen(${ctx.result})`;
|
|
1344
|
+
}
|
|
1345
|
+
// Non-string field
|
|
1346
|
+
throw new Error(
|
|
1347
|
+
`Error: .char_count is only available on strings, not on '${fieldInfo?.type || ctx.previousMemberName}'.`,
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// Get type info
|
|
1352
|
+
const typeInfo = ctx.resolvedIdentifier
|
|
1353
|
+
? input.typeRegistry.get(ctx.resolvedIdentifier)
|
|
1354
|
+
: undefined;
|
|
1355
|
+
|
|
1356
|
+
if (!typeInfo) {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
`Error: Cannot determine .char_count for '${ctx.result}' - type not found in registry.`,
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// Must be a string type
|
|
1363
|
+
if (!typeInfo.isString) {
|
|
1364
|
+
throw new Error(
|
|
1365
|
+
`Error: .char_count is only available on strings, not on '${typeInfo.baseType}'.`,
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
effects.push({ type: "include", header: "string" });
|
|
1370
|
+
|
|
1371
|
+
// Check length cache first
|
|
1372
|
+
if (
|
|
1373
|
+
ctx.resolvedIdentifier &&
|
|
1374
|
+
state.lengthCache?.has(ctx.resolvedIdentifier)
|
|
1375
|
+
) {
|
|
1376
|
+
return state.lengthCache.get(ctx.resolvedIdentifier)!;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
const target = ctx.resolvedIdentifier ?? ctx.result;
|
|
1380
|
+
return `strlen(${target})`;
|
|
1381
|
+
};
|
|
1382
|
+
|
|
771
1383
|
// ========================================================================
|
|
772
1384
|
// Member Access
|
|
773
1385
|
// ========================================================================
|