c-next 0.2.16 → 0.2.17
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/README.md +16 -0
- package/dist/index.js +1347 -414
- package/dist/index.js.map +3 -3
- package/grammar/CNext.g4 +4 -0
- package/package.json +5 -1
- package/src/transpiler/Transpiler.ts +90 -22
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
- package/src/transpiler/logic/symbols/SymbolTable.ts +17 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +6 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +71 -6
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -804,4 +804,284 @@ describe("HeaderGeneratorUtils", () => {
|
|
|
804
804
|
expect(lines).toContain("#endif /* MY_FILE_H */");
|
|
805
805
|
});
|
|
806
806
|
});
|
|
807
|
+
|
|
808
|
+
describe("generateCallbackStructForwardDecls", () => {
|
|
809
|
+
it("returns empty array when no callback types provided", () => {
|
|
810
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
811
|
+
[],
|
|
812
|
+
);
|
|
813
|
+
expect(result).toEqual([]);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
it("returns empty array when callback types is undefined", () => {
|
|
817
|
+
const input = {
|
|
818
|
+
structFields: new Map(),
|
|
819
|
+
structFieldDimensions: new Map(),
|
|
820
|
+
enumMembers: new Map(),
|
|
821
|
+
bitmapBackingType: new Map(),
|
|
822
|
+
bitmapFields: new Map(),
|
|
823
|
+
callbackTypes: undefined,
|
|
824
|
+
};
|
|
825
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
826
|
+
[],
|
|
827
|
+
input,
|
|
828
|
+
);
|
|
829
|
+
expect(result).toEqual([]);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it("generates forward declarations for struct parameters", () => {
|
|
833
|
+
const structs = [makeSymbol({ name: "DataStruct", kind: "struct" })];
|
|
834
|
+
const input = {
|
|
835
|
+
structFields: new Map(),
|
|
836
|
+
structFieldDimensions: new Map(),
|
|
837
|
+
enumMembers: new Map(),
|
|
838
|
+
bitmapBackingType: new Map(),
|
|
839
|
+
bitmapFields: new Map(),
|
|
840
|
+
callbackTypes: new Map([
|
|
841
|
+
[
|
|
842
|
+
"processData",
|
|
843
|
+
{
|
|
844
|
+
typedefName: "processData_t",
|
|
845
|
+
returnType: "void",
|
|
846
|
+
parameters: [
|
|
847
|
+
{ type: "DataStruct", isStruct: true },
|
|
848
|
+
{ type: "uint32_t", isStruct: false },
|
|
849
|
+
],
|
|
850
|
+
},
|
|
851
|
+
],
|
|
852
|
+
]),
|
|
853
|
+
};
|
|
854
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
855
|
+
structs,
|
|
856
|
+
input,
|
|
857
|
+
);
|
|
858
|
+
expect(result).toContain("typedef struct DataStruct DataStruct;");
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
it("deduplicates struct forward declarations", () => {
|
|
862
|
+
const structs = [makeSymbol({ name: "MyStruct", kind: "struct" })];
|
|
863
|
+
const input = {
|
|
864
|
+
structFields: new Map(),
|
|
865
|
+
structFieldDimensions: new Map(),
|
|
866
|
+
enumMembers: new Map(),
|
|
867
|
+
bitmapBackingType: new Map(),
|
|
868
|
+
bitmapFields: new Map(),
|
|
869
|
+
callbackTypes: new Map([
|
|
870
|
+
[
|
|
871
|
+
"handler1",
|
|
872
|
+
{
|
|
873
|
+
typedefName: "handler1_t",
|
|
874
|
+
returnType: "void",
|
|
875
|
+
parameters: [{ type: "MyStruct", isStruct: true }],
|
|
876
|
+
},
|
|
877
|
+
],
|
|
878
|
+
[
|
|
879
|
+
"handler2",
|
|
880
|
+
{
|
|
881
|
+
typedefName: "handler2_t",
|
|
882
|
+
returnType: "void",
|
|
883
|
+
parameters: [{ type: "MyStruct", isStruct: true }],
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
]),
|
|
887
|
+
};
|
|
888
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
889
|
+
structs,
|
|
890
|
+
input,
|
|
891
|
+
);
|
|
892
|
+
const myStructDecls = result.filter((l) => l.includes("MyStruct"));
|
|
893
|
+
expect(myStructDecls.length).toBe(1);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it("ignores non-struct parameters", () => {
|
|
897
|
+
const input = {
|
|
898
|
+
structFields: new Map(),
|
|
899
|
+
structFieldDimensions: new Map(),
|
|
900
|
+
enumMembers: new Map(),
|
|
901
|
+
bitmapBackingType: new Map(),
|
|
902
|
+
bitmapFields: new Map(),
|
|
903
|
+
callbackTypes: new Map([
|
|
904
|
+
[
|
|
905
|
+
"handler",
|
|
906
|
+
{
|
|
907
|
+
typedefName: "handler_t",
|
|
908
|
+
returnType: "uint32_t",
|
|
909
|
+
parameters: [
|
|
910
|
+
{ type: "uint32_t", isStruct: false },
|
|
911
|
+
{ type: "bool", isStruct: false },
|
|
912
|
+
],
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
]),
|
|
916
|
+
};
|
|
917
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
918
|
+
[],
|
|
919
|
+
input,
|
|
920
|
+
);
|
|
921
|
+
expect(result).toEqual([]);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it("only includes local structs in forward declarations", () => {
|
|
925
|
+
// ExternalStruct is not in the local structs list
|
|
926
|
+
const structs = [makeSymbol({ name: "LocalStruct", kind: "struct" })];
|
|
927
|
+
const input = {
|
|
928
|
+
structFields: new Map(),
|
|
929
|
+
structFieldDimensions: new Map(),
|
|
930
|
+
enumMembers: new Map(),
|
|
931
|
+
bitmapBackingType: new Map(),
|
|
932
|
+
bitmapFields: new Map(),
|
|
933
|
+
callbackTypes: new Map([
|
|
934
|
+
[
|
|
935
|
+
"handler",
|
|
936
|
+
{
|
|
937
|
+
typedefName: "handler_t",
|
|
938
|
+
returnType: "void",
|
|
939
|
+
parameters: [
|
|
940
|
+
{ type: "LocalStruct", isStruct: true },
|
|
941
|
+
{ type: "ExternalStruct", isStruct: true },
|
|
942
|
+
],
|
|
943
|
+
},
|
|
944
|
+
],
|
|
945
|
+
]),
|
|
946
|
+
};
|
|
947
|
+
const result = HeaderGeneratorUtils.generateCallbackStructForwardDecls(
|
|
948
|
+
structs,
|
|
949
|
+
input,
|
|
950
|
+
);
|
|
951
|
+
expect(result).toContain("typedef struct LocalStruct LocalStruct;");
|
|
952
|
+
expect(result).not.toContain("ExternalStruct");
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
describe("generateCallbackTypedefSection", () => {
|
|
957
|
+
it("returns empty array when no callback types provided", () => {
|
|
958
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection();
|
|
959
|
+
expect(result).toEqual([]);
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
it("returns empty array when callback types is undefined", () => {
|
|
963
|
+
const input = {
|
|
964
|
+
structFields: new Map(),
|
|
965
|
+
structFieldDimensions: new Map(),
|
|
966
|
+
enumMembers: new Map(),
|
|
967
|
+
bitmapBackingType: new Map(),
|
|
968
|
+
bitmapFields: new Map(),
|
|
969
|
+
callbackTypes: undefined,
|
|
970
|
+
};
|
|
971
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
972
|
+
input,
|
|
973
|
+
false,
|
|
974
|
+
);
|
|
975
|
+
expect(result).toEqual([]);
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
it("generates C-style function pointer typedef", () => {
|
|
979
|
+
const input = {
|
|
980
|
+
structFields: new Map(),
|
|
981
|
+
structFieldDimensions: new Map(),
|
|
982
|
+
enumMembers: new Map(),
|
|
983
|
+
bitmapBackingType: new Map(),
|
|
984
|
+
bitmapFields: new Map(),
|
|
985
|
+
callbackTypes: new Map([
|
|
986
|
+
[
|
|
987
|
+
"processU32",
|
|
988
|
+
{
|
|
989
|
+
typedefName: "processU32_t",
|
|
990
|
+
returnType: "uint32_t",
|
|
991
|
+
parameters: [{ type: "uint32_t", isStruct: false }],
|
|
992
|
+
},
|
|
993
|
+
],
|
|
994
|
+
]),
|
|
995
|
+
};
|
|
996
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
997
|
+
input,
|
|
998
|
+
false,
|
|
999
|
+
);
|
|
1000
|
+
expect(result.join("\n")).toContain(
|
|
1001
|
+
"typedef uint32_t (*processU32_t)(uint32_t);",
|
|
1002
|
+
);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
it("generates C++ reference for struct parameters", () => {
|
|
1006
|
+
const input = {
|
|
1007
|
+
structFields: new Map(),
|
|
1008
|
+
structFieldDimensions: new Map(),
|
|
1009
|
+
enumMembers: new Map(),
|
|
1010
|
+
bitmapBackingType: new Map(),
|
|
1011
|
+
bitmapFields: new Map(),
|
|
1012
|
+
callbackTypes: new Map([
|
|
1013
|
+
[
|
|
1014
|
+
"processPoint",
|
|
1015
|
+
{
|
|
1016
|
+
typedefName: "processPoint_t",
|
|
1017
|
+
returnType: "uint32_t",
|
|
1018
|
+
parameters: [{ type: "Point", isStruct: true }],
|
|
1019
|
+
},
|
|
1020
|
+
],
|
|
1021
|
+
]),
|
|
1022
|
+
};
|
|
1023
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
1024
|
+
input,
|
|
1025
|
+
true,
|
|
1026
|
+
);
|
|
1027
|
+
expect(result.join("\n")).toContain("Point&");
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it("generates C pointer for struct parameters", () => {
|
|
1031
|
+
const input = {
|
|
1032
|
+
structFields: new Map(),
|
|
1033
|
+
structFieldDimensions: new Map(),
|
|
1034
|
+
enumMembers: new Map(),
|
|
1035
|
+
bitmapBackingType: new Map(),
|
|
1036
|
+
bitmapFields: new Map(),
|
|
1037
|
+
callbackTypes: new Map([
|
|
1038
|
+
[
|
|
1039
|
+
"processPoint",
|
|
1040
|
+
{
|
|
1041
|
+
typedefName: "processPoint_t",
|
|
1042
|
+
returnType: "uint32_t",
|
|
1043
|
+
parameters: [{ type: "Point", isStruct: true }],
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
]),
|
|
1047
|
+
};
|
|
1048
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
1049
|
+
input,
|
|
1050
|
+
false,
|
|
1051
|
+
);
|
|
1052
|
+
expect(result.join("\n")).toContain("Point*");
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it("handles multiple parameters", () => {
|
|
1056
|
+
const input = {
|
|
1057
|
+
structFields: new Map(),
|
|
1058
|
+
structFieldDimensions: new Map(),
|
|
1059
|
+
enumMembers: new Map(),
|
|
1060
|
+
bitmapBackingType: new Map(),
|
|
1061
|
+
bitmapFields: new Map(),
|
|
1062
|
+
callbackTypes: new Map([
|
|
1063
|
+
[
|
|
1064
|
+
"multiParam",
|
|
1065
|
+
{
|
|
1066
|
+
typedefName: "multiParam_t",
|
|
1067
|
+
returnType: "void",
|
|
1068
|
+
parameters: [
|
|
1069
|
+
{ type: "uint32_t", isStruct: false },
|
|
1070
|
+
{ type: "Point", isStruct: true },
|
|
1071
|
+
{ type: "bool", isStruct: false },
|
|
1072
|
+
],
|
|
1073
|
+
},
|
|
1074
|
+
],
|
|
1075
|
+
]),
|
|
1076
|
+
};
|
|
1077
|
+
const result = HeaderGeneratorUtils.generateCallbackTypedefSection(
|
|
1078
|
+
input,
|
|
1079
|
+
false,
|
|
1080
|
+
);
|
|
1081
|
+
const typedef = result.find((l) => l.includes("multiParam_t"));
|
|
1082
|
+
expect(typedef).toContain("uint32_t");
|
|
1083
|
+
expect(typedef).toContain("Point*");
|
|
1084
|
+
expect(typedef).toContain("bool");
|
|
1085
|
+
});
|
|
1086
|
+
});
|
|
807
1087
|
});
|
|
@@ -31,6 +31,19 @@ interface IHeaderTypeInput {
|
|
|
31
31
|
string,
|
|
32
32
|
ReadonlyMap<string, { readonly offset: number; readonly width: number }>
|
|
33
33
|
>;
|
|
34
|
+
|
|
35
|
+
/** Callback types: functionName -> typedef info for header generation */
|
|
36
|
+
readonly callbackTypes?: ReadonlyMap<
|
|
37
|
+
string,
|
|
38
|
+
{
|
|
39
|
+
readonly typedefName: string;
|
|
40
|
+
readonly returnType: string;
|
|
41
|
+
readonly parameters: ReadonlyArray<{
|
|
42
|
+
readonly type: string;
|
|
43
|
+
readonly isStruct: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
>;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
export default IHeaderTypeInput;
|
|
@@ -11,6 +11,52 @@ import CppNamespaceUtils from "../../../../utils/CppNamespaceUtils";
|
|
|
11
11
|
|
|
12
12
|
const { mapType } = typeUtils;
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the C type for a struct field, checking for callback types first.
|
|
16
|
+
*/
|
|
17
|
+
function resolveFieldCType(fieldType: string, input: IHeaderTypeInput): string {
|
|
18
|
+
// ADR-029: Check if field type is a callback type and use typedef name
|
|
19
|
+
const callbackInfo = input.callbackTypes?.get(fieldType);
|
|
20
|
+
if (callbackInfo) {
|
|
21
|
+
return callbackInfo.typedefName;
|
|
22
|
+
}
|
|
23
|
+
// Issue #502/#522: Convert C++ namespace types from _ to :: format
|
|
24
|
+
const convertedType = CppNamespaceUtils.convertToCppNamespace(
|
|
25
|
+
fieldType,
|
|
26
|
+
input.symbolTable,
|
|
27
|
+
);
|
|
28
|
+
return mapType(convertedType);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate the field line for a struct member, handling embedded dimensions.
|
|
33
|
+
*
|
|
34
|
+
* Issue #461: Handle string<N> types which map to char[N+1]
|
|
35
|
+
* The embedded dimension must come after array dimensions in C syntax.
|
|
36
|
+
* Example: string<64> arr[4] -> char arr[4][65], not char[65] arr[4]
|
|
37
|
+
*/
|
|
38
|
+
function generateFieldLine(
|
|
39
|
+
fieldName: string,
|
|
40
|
+
cType: string,
|
|
41
|
+
dims: readonly number[] | undefined,
|
|
42
|
+
): string {
|
|
43
|
+
const dimSuffix =
|
|
44
|
+
dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
|
|
45
|
+
const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
|
|
46
|
+
|
|
47
|
+
if (!embeddedMatch) {
|
|
48
|
+
return ` ${cType} ${fieldName}${dimSuffix};`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const baseType = embeddedMatch[1];
|
|
52
|
+
const embeddedDim = embeddedMatch[2];
|
|
53
|
+
// When dims are provided, they already include string capacity from StructCollector
|
|
54
|
+
if (dims && dims.length > 0) {
|
|
55
|
+
return ` ${baseType} ${fieldName}${dimSuffix};`;
|
|
56
|
+
}
|
|
57
|
+
return ` ${baseType} ${fieldName}[${embeddedDim}];`;
|
|
58
|
+
}
|
|
59
|
+
|
|
14
60
|
/**
|
|
15
61
|
* Generate a C typedef struct declaration for the given struct name.
|
|
16
62
|
*
|
|
@@ -41,35 +87,9 @@ function generateStructHeader(name: string, input: IHeaderTypeInput): string {
|
|
|
41
87
|
|
|
42
88
|
// Iterate fields in insertion order (Map preserves order)
|
|
43
89
|
for (const [fieldName, fieldType] of fields) {
|
|
44
|
-
|
|
45
|
-
const convertedType = CppNamespaceUtils.convertToCppNamespace(
|
|
46
|
-
fieldType,
|
|
47
|
-
input.symbolTable,
|
|
48
|
-
);
|
|
49
|
-
const cType = mapType(convertedType);
|
|
90
|
+
const cType = resolveFieldCType(fieldType, input);
|
|
50
91
|
const dims = dimensions?.get(fieldName);
|
|
51
|
-
|
|
52
|
-
dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
|
|
53
|
-
|
|
54
|
-
// Issue #461: Handle string<N> types which map to char[N+1]
|
|
55
|
-
// The embedded dimension must come after array dimensions in C syntax
|
|
56
|
-
// Example: string<64> arr[4] -> char arr[4][65], not char[65] arr[4]
|
|
57
|
-
//
|
|
58
|
-
// Fix: When dims already include the string capacity (from StructCollector),
|
|
59
|
-
// use dimSuffix directly. Only extract embedded dim when no dims provided.
|
|
60
|
-
const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
|
|
61
|
-
if (embeddedMatch) {
|
|
62
|
-
const baseType = embeddedMatch[1];
|
|
63
|
-
const embeddedDim = embeddedMatch[2];
|
|
64
|
-
// dims already includes string capacity, so just use dimSuffix
|
|
65
|
-
if (dims && dims.length > 0) {
|
|
66
|
-
lines.push(` ${baseType} ${fieldName}${dimSuffix};`);
|
|
67
|
-
} else {
|
|
68
|
-
lines.push(` ${baseType} ${fieldName}[${embeddedDim}];`);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
lines.push(` ${cType} ${fieldName}${dimSuffix};`);
|
|
72
|
-
}
|
|
92
|
+
lines.push(generateFieldLine(fieldName, cType, dims));
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
lines.push(`} ${name};`);
|
|
@@ -217,6 +217,12 @@ export default class CodeGenState {
|
|
|
217
217
|
/** Whether we're inside a function body */
|
|
218
218
|
static inFunctionBody: boolean = false;
|
|
219
219
|
|
|
220
|
+
/** Whether we're generating the RHS of a variable declaration initializer.
|
|
221
|
+
* When true, struct literals use { .field = value } instead of (Type){ .field = value }
|
|
222
|
+
* because plain designated initializers are valid C99 at any scope, while compound
|
|
223
|
+
* literals are not constant expressions and fail at file scope on GCC < 13. */
|
|
224
|
+
static inDeclarationInit: boolean = false;
|
|
225
|
+
|
|
220
226
|
/** Expected type for struct initializers and enum inference */
|
|
221
227
|
static expectedType: string | null = null;
|
|
222
228
|
|
|
@@ -381,6 +387,7 @@ export default class CodeGenState {
|
|
|
381
387
|
// Generation state
|
|
382
388
|
this.indentLevel = 0;
|
|
383
389
|
this.inFunctionBody = false;
|
|
390
|
+
this.inDeclarationInit = false;
|
|
384
391
|
this.expectedType = null;
|
|
385
392
|
this.suppressBareEnumResolution = false;
|
|
386
393
|
this.mainArgsName = null;
|
|
@@ -476,6 +483,49 @@ export default class CodeGenState {
|
|
|
476
483
|
}
|
|
477
484
|
}
|
|
478
485
|
|
|
486
|
+
/** Execute fn with inDeclarationInit=true, restoring prior value on exit. */
|
|
487
|
+
static withDeclarationInit<T>(fn: () => T): T {
|
|
488
|
+
const saved = this.inDeclarationInit;
|
|
489
|
+
this.inDeclarationInit = true;
|
|
490
|
+
try {
|
|
491
|
+
return fn();
|
|
492
|
+
} finally {
|
|
493
|
+
this.inDeclarationInit = saved;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Execute fn with inDeclarationInit=false, restoring prior value on exit.
|
|
498
|
+
* Used in sub-expression contexts (function args, ternary arms) where
|
|
499
|
+
* plain designated initializers are not valid C. */
|
|
500
|
+
static withoutDeclarationInit<T>(fn: () => T): T {
|
|
501
|
+
const saved = this.inDeclarationInit;
|
|
502
|
+
this.inDeclarationInit = false;
|
|
503
|
+
try {
|
|
504
|
+
return fn();
|
|
505
|
+
} finally {
|
|
506
|
+
this.inDeclarationInit = saved;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Execute fn with expectedType=null, restoring prior value on exit.
|
|
512
|
+
* Issue #1032: Used in comparison contexts (relational/equality expressions)
|
|
513
|
+
* where MISRA 7.2 U suffix should NOT be applied - comparing `i32 < 0`
|
|
514
|
+
* should not generate `signedIdx < 0U` which changes comparison semantics.
|
|
515
|
+
*/
|
|
516
|
+
static withoutExpectedType<T>(fn: () => T): T {
|
|
517
|
+
const savedType = this.expectedType;
|
|
518
|
+
const savedSuppress = this.suppressBareEnumResolution;
|
|
519
|
+
this.expectedType = null;
|
|
520
|
+
this.suppressBareEnumResolution = false;
|
|
521
|
+
try {
|
|
522
|
+
return fn();
|
|
523
|
+
} finally {
|
|
524
|
+
this.expectedType = savedType;
|
|
525
|
+
this.suppressBareEnumResolution = savedSuppress;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
479
529
|
// ===========================================================================
|
|
480
530
|
// CONVENIENCE LOOKUP METHODS
|
|
481
531
|
// ===========================================================================
|
|
@@ -1136,14 +1186,29 @@ export default class CodeGenState {
|
|
|
1136
1186
|
}
|
|
1137
1187
|
|
|
1138
1188
|
/**
|
|
1139
|
-
* Check if
|
|
1140
|
-
* Used during
|
|
1189
|
+
* Check if generated code accesses an opaque scope variable (and is thus
|
|
1190
|
+
* already a pointer). Used during argument generation to decide whether an
|
|
1191
|
+
* address-of (&) prefix is needed.
|
|
1141
1192
|
*
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
1193
|
+
* Handles two forms:
|
|
1194
|
+
* - Direct access: "MyScope_widget" → the handle itself (pointer)
|
|
1195
|
+
* - Array-element access: "MyScope_widgets[i]" → an element of an opaque
|
|
1196
|
+
* handle array, which is itself a pointer (Issue #996)
|
|
1197
|
+
*
|
|
1198
|
+
* @param generatedCode - The generated access expression (e.g. "UI_widgets[i]")
|
|
1199
|
+
* @returns true if this resolves to an opaque scope variable (already a pointer)
|
|
1144
1200
|
*/
|
|
1145
|
-
static
|
|
1146
|
-
|
|
1201
|
+
static isOpaqueScopeVariableAccess(generatedCode: string): boolean {
|
|
1202
|
+
if (this.opaqueScopeVariables.has(generatedCode)) {
|
|
1203
|
+
return true;
|
|
1204
|
+
}
|
|
1205
|
+
// Issue #996: An element of an opaque-handle array is already a pointer.
|
|
1206
|
+
// Match on the base array name that precedes the subscript.
|
|
1207
|
+
const bracketIndex = generatedCode.indexOf("[");
|
|
1208
|
+
if (bracketIndex === -1) {
|
|
1209
|
+
return false;
|
|
1210
|
+
}
|
|
1211
|
+
return this.opaqueScopeVariables.has(generatedCode.slice(0, bracketIndex));
|
|
1147
1212
|
}
|
|
1148
1213
|
|
|
1149
1214
|
// ===========================================================================
|