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
|
@@ -6,14 +6,16 @@ import * as Parser from "../../../parser/grammar/CNextParser";
|
|
|
6
6
|
import CNEXT_TO_C_TYPE_MAP from "../../../../../utils/constants/TypeMappings";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Common interface for type contexts that share the same type accessors.
|
|
10
|
+
* Both TypeContext and ArrayTypeContext have these methods.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
):
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
interface ITypeAccessors {
|
|
13
|
+
primitiveType(): Parser.PrimitiveTypeContext | null;
|
|
14
|
+
userType(): Parser.UserTypeContext | null;
|
|
15
|
+
stringType(): Parser.StringTypeContext | null;
|
|
16
|
+
scopedType(): Parser.ScopedTypeContext | null;
|
|
17
|
+
qualifiedType(): Parser.QualifiedTypeContext | null;
|
|
18
|
+
globalType(): Parser.GlobalTypeContext | null;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -25,19 +27,50 @@ function resolveStringType(stringCtx: Parser.StringTypeContext): string {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
|
-
*
|
|
30
|
+
* Dispatch type resolution for contexts that share common type accessors.
|
|
31
|
+
* Handles scoped, qualified, global, primitive, string, and user types.
|
|
32
|
+
* Used by both bare type contexts and array element type contexts.
|
|
33
|
+
*
|
|
34
|
+
* @returns The resolved type name, or null if no matching type accessor found
|
|
29
35
|
*/
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
function dispatchTypeResolution(
|
|
37
|
+
accessors: ITypeAccessors,
|
|
38
|
+
scopeName?: string,
|
|
39
|
+
): string | null {
|
|
40
|
+
// Handle this.Type for scoped types (e.g., this.State -> Motor_State)
|
|
41
|
+
if (accessors.scopedType()) {
|
|
42
|
+
const typeName = accessors.scopedType()!.IDENTIFIER().getText();
|
|
43
|
+
return scopeName ? `${scopeName}_${typeName}` : typeName;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle global.Type for global types inside scope
|
|
47
|
+
// global.ECategory -> ECategory (just the type name, no scope prefix)
|
|
48
|
+
if (accessors.globalType()) {
|
|
49
|
+
return accessors.globalType()!.IDENTIFIER().getText();
|
|
33
50
|
}
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
|
|
52
|
+
// Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
|
|
53
|
+
if (accessors.qualifiedType()) {
|
|
54
|
+
const identifiers = accessors.qualifiedType()!.IDENTIFIER();
|
|
55
|
+
return identifiers.map((id) => id.getText()).join("_");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Handle user-defined types
|
|
59
|
+
if (accessors.userType()) {
|
|
60
|
+
return accessors.userType()!.getText();
|
|
36
61
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
62
|
+
|
|
63
|
+
// Handle primitive types
|
|
64
|
+
if (accessors.primitiveType()) {
|
|
65
|
+
return accessors.primitiveType()!.getText();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Handle string types - preserve capacity for validation (Issue #139)
|
|
69
|
+
if (accessors.stringType()) {
|
|
70
|
+
return resolveStringType(accessors.stringType()!);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
41
74
|
}
|
|
42
75
|
|
|
43
76
|
class TypeUtils {
|
|
@@ -56,42 +89,23 @@ class TypeUtils {
|
|
|
56
89
|
): string {
|
|
57
90
|
if (!ctx) return "void";
|
|
58
91
|
|
|
59
|
-
// Handle this.Type for scoped types (e.g., this.State -> Motor_State)
|
|
60
|
-
if (ctx.scopedType()) {
|
|
61
|
-
return resolveScopedType(ctx.scopedType()!, scopeName);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Issue #478: Handle global.Type for global types inside scope
|
|
65
|
-
// global.ECategory -> ECategory (just the type name, no scope prefix)
|
|
66
|
-
if (ctx.globalType()) {
|
|
67
|
-
return ctx.globalType()!.IDENTIFIER().getText();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Handle Scope.Type from outside scope (e.g., Motor.State -> Motor_State)
|
|
71
|
-
if (ctx.qualifiedType()) {
|
|
72
|
-
const identifiers = ctx.qualifiedType()!.IDENTIFIER();
|
|
73
|
-
return identifiers.map((id) => id.getText()).join("_");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Handle user-defined types
|
|
77
|
-
if (ctx.userType()) {
|
|
78
|
-
return ctx.userType()!.getText();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Handle primitive types
|
|
82
|
-
if (ctx.primitiveType()) {
|
|
83
|
-
return ctx.primitiveType()!.getText();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Handle string types - preserve capacity for validation (Issue #139)
|
|
87
|
-
if (ctx.stringType()) {
|
|
88
|
-
return resolveStringType(ctx.stringType()!);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
92
|
// Handle arrayType: Type[size] - extract the inner type without dimension
|
|
92
93
|
// The dimension is tracked separately in arrayDimensions
|
|
93
94
|
if (ctx.arrayType()) {
|
|
94
|
-
|
|
95
|
+
const result = dispatchTypeResolution(ctx.arrayType()!, scopeName);
|
|
96
|
+
if (result !== null) {
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
// Fallback for unrecognized array types - strip the dimension part
|
|
100
|
+
const text = ctx.arrayType()!.getText();
|
|
101
|
+
const bracketIdx = text.indexOf("[");
|
|
102
|
+
return bracketIdx > 0 ? text.substring(0, bracketIdx) : text;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Non-array types - dispatch directly
|
|
106
|
+
const result = dispatchTypeResolution(ctx, scopeName);
|
|
107
|
+
if (result !== null) {
|
|
108
|
+
return result;
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
// Fallback
|
|
@@ -726,6 +726,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
726
726
|
* Part of IOrchestrator interface.
|
|
727
727
|
* ADR-045: Used to detect string comparisons and generate strcmp().
|
|
728
728
|
* Issue #137: Extended to handle array element access (e.g., names[0])
|
|
729
|
+
* Issue #1030: Extended to handle struct member access (e.g., person.name)
|
|
729
730
|
*/
|
|
730
731
|
isStringExpression(ctx: Parser.RelationalExpressionContext): boolean {
|
|
731
732
|
const text = ctx.getText();
|
|
@@ -743,6 +744,11 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
743
744
|
}
|
|
744
745
|
}
|
|
745
746
|
|
|
747
|
+
// Issue #1030: Check for struct member access (e.g., person.name)
|
|
748
|
+
if (this._isStructMemberStringExpression(text)) {
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
|
|
746
752
|
// Issue #137: Check for array element access (e.g., names[0], arr[i])
|
|
747
753
|
return this._isArrayAccessStringExpression(text);
|
|
748
754
|
}
|
|
@@ -800,6 +806,59 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
800
806
|
);
|
|
801
807
|
}
|
|
802
808
|
|
|
809
|
+
/**
|
|
810
|
+
* Check if struct member access expression evaluates to a string.
|
|
811
|
+
* Issue #1030: Handles patterns like person.name, config.key
|
|
812
|
+
*/
|
|
813
|
+
private _isStructMemberStringExpression(text: string): boolean {
|
|
814
|
+
// Pattern: identifier.identifier (simple member access)
|
|
815
|
+
// Must not end with a property that returns a number
|
|
816
|
+
if (
|
|
817
|
+
text.endsWith(".char_count") ||
|
|
818
|
+
text.endsWith(".capacity") ||
|
|
819
|
+
text.endsWith(".size") ||
|
|
820
|
+
text.endsWith(".length") ||
|
|
821
|
+
text.endsWith(".bit_length") ||
|
|
822
|
+
text.endsWith(".byte_length") ||
|
|
823
|
+
text.endsWith(".element_count")
|
|
824
|
+
) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Match simple struct.member pattern
|
|
829
|
+
const memberMatch = /^([a-zA-Z_]\w*)\.([a-zA-Z_]\w*)$/.exec(text);
|
|
830
|
+
if (!memberMatch) {
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const [, varName, fieldName] = memberMatch;
|
|
835
|
+
|
|
836
|
+
// Get the struct variable's type
|
|
837
|
+
const typeInfo = CodeGenState.getVariableTypeInfo(varName);
|
|
838
|
+
if (!typeInfo) {
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Get the struct type name - it might be directly the baseType
|
|
843
|
+
// or we might need to look it up by the variable's type
|
|
844
|
+
const structTypeName = typeInfo.baseType;
|
|
845
|
+
if (!structTypeName) {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Look up the field type from the struct
|
|
850
|
+
const fieldType = CodeGenState.getStructFieldType(
|
|
851
|
+
structTypeName,
|
|
852
|
+
fieldName,
|
|
853
|
+
);
|
|
854
|
+
if (!fieldType) {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Check if the field is a string type (e.g., "string<64>")
|
|
859
|
+
return fieldType.startsWith("string");
|
|
860
|
+
}
|
|
861
|
+
|
|
803
862
|
/**
|
|
804
863
|
* Get type of additive expression.
|
|
805
864
|
* Part of IOrchestrator interface - delegates to private implementation.
|
|
@@ -1311,9 +1370,10 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1311
1370
|
* ADR-017: Handle enum types by initializing to first member
|
|
1312
1371
|
*/
|
|
1313
1372
|
getZeroInitializer(typeCtx: Parser.TypeContext, isArray: boolean): string {
|
|
1314
|
-
// Issue #379:
|
|
1373
|
+
// Issue #379 / #1004: arrays zero-init with the aggregate brace ({} in
|
|
1374
|
+
// C++, {0} in C) regardless of element type.
|
|
1315
1375
|
if (isArray) {
|
|
1316
|
-
return this.
|
|
1376
|
+
return this._getAggregateZeroInitBrace();
|
|
1317
1377
|
}
|
|
1318
1378
|
|
|
1319
1379
|
// Handle named types (scoped, global, qualified, user)
|
|
@@ -1323,11 +1383,10 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1323
1383
|
if (CodeGenState.symbols!.knownEnums.has(resolved.name)) {
|
|
1324
1384
|
return this._getEnumZeroValue(resolved.name, resolved.separator);
|
|
1325
1385
|
}
|
|
1326
|
-
//
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
return "{0}";
|
|
1386
|
+
// Issue #1004: struct/class zero-init. C++ value-initialization ({})
|
|
1387
|
+
// works for every aggregate (including ones whose first field is an
|
|
1388
|
+
// enum, where {0} is an invalid int->enum narrowing); C uses {0}.
|
|
1389
|
+
return this._getAggregateZeroInitBrace();
|
|
1331
1390
|
}
|
|
1332
1391
|
|
|
1333
1392
|
// Issue #295: C++ template types use value initialization {}
|
|
@@ -1335,6 +1394,11 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1335
1394
|
return "{}";
|
|
1336
1395
|
}
|
|
1337
1396
|
|
|
1397
|
+
// Issue #1019: string<N> types use empty string initializer
|
|
1398
|
+
if (typeCtx.stringType()) {
|
|
1399
|
+
return '""';
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1338
1402
|
// Primitive types use lookup map
|
|
1339
1403
|
if (typeCtx.primitiveType()) {
|
|
1340
1404
|
const primType = typeCtx.primitiveType()!.getText();
|
|
@@ -1490,9 +1554,9 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1490
1554
|
if (p.isArray) {
|
|
1491
1555
|
// Array parameters: type name[]
|
|
1492
1556
|
return `${constMod}${p.type} ${p.name}${p.arrayDims}`;
|
|
1493
|
-
} else if (p.
|
|
1494
|
-
// ADR-006:
|
|
1495
|
-
//
|
|
1557
|
+
} else if (p.isStruct) {
|
|
1558
|
+
// ADR-006: Struct parameters become pointers (C) or references (C++)
|
|
1559
|
+
// Only struct types get pointer/reference semantics, not primitives
|
|
1496
1560
|
const ptrOrRef = this.isCppMode() ? "&" : "*";
|
|
1497
1561
|
return `${constMod}${p.type}${ptrOrRef}`;
|
|
1498
1562
|
} else {
|
|
@@ -1776,7 +1840,17 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1776
1840
|
}
|
|
1777
1841
|
|
|
1778
1842
|
if (ctx.IDENTIFIER()) {
|
|
1779
|
-
|
|
1843
|
+
const id = ctx.IDENTIFIER()!.getText();
|
|
1844
|
+
// Issue #1011: break/continue are not part of C-Next - use structured conditions
|
|
1845
|
+
// ADR-026 (Status: Rejected) explicitly excludes break/continue from the language
|
|
1846
|
+
if (id === "break" || id === "continue") {
|
|
1847
|
+
const line = ctx.start?.line ?? 0;
|
|
1848
|
+
const col = ctx.start?.column ?? 0;
|
|
1849
|
+
throw new Error(
|
|
1850
|
+
`${line}:${col} error[E0703]: '${id}' is not supported in C-Next - use structured conditions instead`,
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
return this._resolveIdentifierExpression(id);
|
|
1780
1854
|
}
|
|
1781
1855
|
if (ctx.literal()) {
|
|
1782
1856
|
return this._generateLiteralExpression(ctx.literal()!);
|
|
@@ -2085,15 +2159,6 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2085
2159
|
return identifiers.join("_");
|
|
2086
2160
|
}
|
|
2087
2161
|
|
|
2088
|
-
/**
|
|
2089
|
-
* Issue #304: Check if a type name is from a C++ header
|
|
2090
|
-
* Used to determine whether to use {} or {0} for initialization.
|
|
2091
|
-
* C++ types with constructors may fail with {0} but work with {}.
|
|
2092
|
-
*/
|
|
2093
|
-
private isCppType(typeName: string): boolean {
|
|
2094
|
-
return SymbolLookupHelper.isCppType(CodeGenState.symbolTable, typeName);
|
|
2095
|
-
}
|
|
2096
|
-
|
|
2097
2162
|
/**
|
|
2098
2163
|
* Generate C code from a C-Next program
|
|
2099
2164
|
* @param tree The parsed C-Next program
|
|
@@ -2689,6 +2754,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2689
2754
|
type: string;
|
|
2690
2755
|
isConst: boolean;
|
|
2691
2756
|
isPointer: boolean;
|
|
2757
|
+
isStruct: boolean;
|
|
2692
2758
|
isArray: boolean;
|
|
2693
2759
|
arrayDims: string;
|
|
2694
2760
|
}> = [];
|
|
@@ -2704,6 +2770,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2704
2770
|
|
|
2705
2771
|
// ADR-029: Check if parameter type is itself a callback type
|
|
2706
2772
|
const isCallbackParam = CodeGenState.callbackTypes.has(typeName);
|
|
2773
|
+
// ADR-006: Check if parameter type is a struct (for pointer/reference semantics)
|
|
2774
|
+
const isStruct = this.isStructType(typeName);
|
|
2707
2775
|
|
|
2708
2776
|
let paramType: string;
|
|
2709
2777
|
let isPointer: boolean;
|
|
@@ -2715,8 +2783,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2715
2783
|
isPointer = false; // Function pointers are already pointers
|
|
2716
2784
|
} else {
|
|
2717
2785
|
paramType = this.generateType(param.type());
|
|
2718
|
-
// ADR-006: Non-array parameters become pointers
|
|
2719
|
-
isPointer = !isArray;
|
|
2786
|
+
// ADR-006: Non-array struct parameters become pointers in C mode
|
|
2787
|
+
isPointer = !isArray && isStruct;
|
|
2720
2788
|
}
|
|
2721
2789
|
|
|
2722
2790
|
let arrayDims: string;
|
|
@@ -2739,6 +2807,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2739
2807
|
type: paramType,
|
|
2740
2808
|
isConst,
|
|
2741
2809
|
isPointer,
|
|
2810
|
+
isStruct,
|
|
2742
2811
|
isArray,
|
|
2743
2812
|
arrayDims,
|
|
2744
2813
|
});
|
|
@@ -3211,7 +3280,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3211
3280
|
decl += this._getStringCapacityDimension(varDecl.type());
|
|
3212
3281
|
|
|
3213
3282
|
if (varDecl.expression()) {
|
|
3214
|
-
decl += ` = ${this.generateExpression(varDecl.expression()!)}`;
|
|
3283
|
+
decl += ` = ${CodeGenState.withDeclarationInit(() => this.generateExpression(varDecl.expression()!))}`;
|
|
3215
3284
|
} else {
|
|
3216
3285
|
// ADR-015: Zero initialization for uninitialized scope variables
|
|
3217
3286
|
decl += ` = ${this.getZeroInitializer(varDecl.type(), isArray)}`;
|
|
@@ -3495,8 +3564,9 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3495
3564
|
);
|
|
3496
3565
|
|
|
3497
3566
|
if (!fieldList) {
|
|
3498
|
-
// Empty initializer: Point {} ->
|
|
3499
|
-
|
|
3567
|
+
// Empty initializer: Point {} -> { 0 } in declaration context, (Point){ 0 } elsewhere
|
|
3568
|
+
if (isCppClass) return "{}";
|
|
3569
|
+
return CodeGenState.inDeclarationInit ? "{ 0 }" : `(${castType}){ 0 }`;
|
|
3500
3570
|
}
|
|
3501
3571
|
|
|
3502
3572
|
// Get field type info for nested initializers
|
|
@@ -3507,28 +3577,10 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3507
3577
|
|
|
3508
3578
|
const fields = fieldList.fieldInitializer().map((field) => {
|
|
3509
3579
|
const fieldName = field.IDENTIFIER().getText();
|
|
3510
|
-
|
|
3511
|
-
// Compute expected type for nested initializers
|
|
3512
|
-
let fieldType: string | undefined;
|
|
3513
|
-
if (structFieldTypes?.has(fieldName)) {
|
|
3514
|
-
// Issue #502: Convert underscore format to correct output format
|
|
3515
|
-
// C-Next struct fields may store C++ types with _ separator (e.g., SeaDash_Parse_ParseResult)
|
|
3516
|
-
// but code generation needs :: for C++ types (e.g., SeaDash::Parse::ParseResult)
|
|
3517
|
-
fieldType = structFieldTypes.get(fieldName)!;
|
|
3518
|
-
if (fieldType.includes("_")) {
|
|
3519
|
-
// Check if this looks like a qualified type (contains _) and convert
|
|
3520
|
-
const parts = fieldType.split("_");
|
|
3521
|
-
if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
|
|
3522
|
-
// It's a C++ namespaced type - convert _ to ::
|
|
3523
|
-
fieldType = parts.join("::");
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
}
|
|
3527
|
-
|
|
3580
|
+
const fieldType = this._resolveFieldType(fieldName, structFieldTypes);
|
|
3528
3581
|
const value = CodeGenState.withExpectedType(fieldType, () =>
|
|
3529
3582
|
this.generateExpression(field.expression()),
|
|
3530
3583
|
);
|
|
3531
|
-
|
|
3532
3584
|
return { fieldName, value };
|
|
3533
3585
|
});
|
|
3534
3586
|
|
|
@@ -3545,6 +3597,23 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3545
3597
|
// For C-Next/C structs, generate designated initializer
|
|
3546
3598
|
const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
|
|
3547
3599
|
|
|
3600
|
+
return this.formatStructInitializer(typeName, castType, fieldInits);
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
private formatStructInitializer(
|
|
3604
|
+
typeName: string,
|
|
3605
|
+
castType: string,
|
|
3606
|
+
fieldInits: string[],
|
|
3607
|
+
): string {
|
|
3608
|
+
const initializer: string = `{ ${fieldInits.join(", ")} }`;
|
|
3609
|
+
|
|
3610
|
+
// In a declaration initializer context, use plain designated initializer — no type cast
|
|
3611
|
+
// prefix needed, and compound literals are not C99 constant expressions so they fail
|
|
3612
|
+
// at file scope on GCC < 13.
|
|
3613
|
+
if (CodeGenState.inDeclarationInit) {
|
|
3614
|
+
return initializer;
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3548
3617
|
// Issue #882: In C++ mode, anonymous structs/unions must use plain brace init.
|
|
3549
3618
|
// Compound literals like (struct { ... }){ ... } create incompatible types in C++
|
|
3550
3619
|
// because each struct { ... } definition creates a distinct nominal type.
|
|
@@ -3552,10 +3621,33 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3552
3621
|
CodeGenState.cppMode &&
|
|
3553
3622
|
(typeName.startsWith("struct {") || typeName.startsWith("union {"))
|
|
3554
3623
|
) {
|
|
3555
|
-
return
|
|
3624
|
+
return initializer;
|
|
3556
3625
|
}
|
|
3557
3626
|
|
|
3558
|
-
|
|
3627
|
+
if (!CodeGenState.inFunctionBody) {
|
|
3628
|
+
return initializer;
|
|
3629
|
+
}
|
|
3630
|
+
|
|
3631
|
+
return `(${castType})${initializer}`;
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
/**
|
|
3635
|
+
* Resolve the C type string for a named struct field, converting C++ underscore-separated
|
|
3636
|
+
* names to :: notation. Returns undefined if the field is not in the type map.
|
|
3637
|
+
* Issue #502: C-Next stores C++ types with _ separator; codegen needs ::.
|
|
3638
|
+
*/
|
|
3639
|
+
private _resolveFieldType(
|
|
3640
|
+
fieldName: string,
|
|
3641
|
+
structFieldTypes: Map<string, string> | undefined,
|
|
3642
|
+
): string | undefined {
|
|
3643
|
+
if (!structFieldTypes?.has(fieldName)) return undefined;
|
|
3644
|
+
const fieldType = structFieldTypes.get(fieldName)!;
|
|
3645
|
+
if (!fieldType.includes("_")) return fieldType;
|
|
3646
|
+
const parts = fieldType.split("_");
|
|
3647
|
+
if (parts.length > 1 && this.isCppScopeSymbol(parts[0])) {
|
|
3648
|
+
return parts.join("::");
|
|
3649
|
+
}
|
|
3650
|
+
return fieldType;
|
|
3559
3651
|
}
|
|
3560
3652
|
|
|
3561
3653
|
/**
|
|
@@ -3776,6 +3868,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3776
3868
|
forceConst,
|
|
3777
3869
|
isTypedefStructType: (t) =>
|
|
3778
3870
|
CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
|
|
3871
|
+
// Issue #995: Opaque handles should not get auto-const
|
|
3872
|
+
isOpaqueType: (t) => CodeGenState.isOpaqueType(t),
|
|
3779
3873
|
});
|
|
3780
3874
|
|
|
3781
3875
|
// Use shared builder with C/C++ mode
|
|
@@ -4102,31 +4196,13 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4102
4196
|
// and _generateConstructorDecl have been extracted to VariableDeclHelper.ts
|
|
4103
4197
|
|
|
4104
4198
|
/**
|
|
4105
|
-
*
|
|
4106
|
-
* Issue #379: C++
|
|
4199
|
+
* Brace initializer that zero-initializes an aggregate (struct or array).
|
|
4200
|
+
* Issue #379 / #1004: C++ uses value-initialization ({}), which is valid for
|
|
4201
|
+
* any aggregate element type (POD, struct, class) including enum-first
|
|
4202
|
+
* structs where {0} is an invalid int->enum narrowing; C uses {0}.
|
|
4107
4203
|
*/
|
|
4108
|
-
private
|
|
4109
|
-
|
|
4110
|
-
if (typeCtx.userType()) {
|
|
4111
|
-
const typeName = typeCtx.userType()!.getText();
|
|
4112
|
-
if (this._needsEmptyBraceInit(typeName)) {
|
|
4113
|
-
return "{}";
|
|
4114
|
-
}
|
|
4115
|
-
}
|
|
4116
|
-
// Also check C-Next style array type (e.g., CppClass[4]) where
|
|
4117
|
-
// the userType is nested inside arrayType.
|
|
4118
|
-
if (typeCtx.arrayType()?.userType()) {
|
|
4119
|
-
const typeName = typeCtx.arrayType()!.userType()!.getText();
|
|
4120
|
-
if (this._needsEmptyBraceInit(typeName)) {
|
|
4121
|
-
return "{}";
|
|
4122
|
-
}
|
|
4123
|
-
}
|
|
4124
|
-
// Template types are always C++ classes
|
|
4125
|
-
if (typeCtx.templateType()) {
|
|
4126
|
-
return "{}";
|
|
4127
|
-
}
|
|
4128
|
-
// Default: POD arrays use {0}
|
|
4129
|
-
return "{0}";
|
|
4204
|
+
private _getAggregateZeroInitBrace(): string {
|
|
4205
|
+
return CodeGenState.cppMode ? "{}" : "{0}";
|
|
4130
4206
|
}
|
|
4131
4207
|
|
|
4132
4208
|
/**
|
|
@@ -4158,20 +4234,19 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4158
4234
|
|
|
4159
4235
|
/**
|
|
4160
4236
|
* Resolve full type name from any TypeContext variant.
|
|
4161
|
-
* Returns { name, separator
|
|
4237
|
+
* Returns { name, separator } or null if not a named type.
|
|
4162
4238
|
* ADR-016: Handles scoped, global, qualified, and user types
|
|
4163
|
-
* checkCppType: only true for userType (original behavior preserved)
|
|
4164
4239
|
*/
|
|
4165
4240
|
private _resolveTypeNameFromContext(
|
|
4166
4241
|
typeCtx: Parser.TypeContext,
|
|
4167
|
-
): { name: string; separator: string
|
|
4242
|
+
): { name: string; separator: string } | null {
|
|
4168
4243
|
// ADR-016: Check for scoped types (this.Type)
|
|
4169
4244
|
if (typeCtx.scopedType()) {
|
|
4170
4245
|
const localName = typeCtx.scopedType()!.IDENTIFIER().getText();
|
|
4171
4246
|
const name = CodeGenState.currentScope
|
|
4172
4247
|
? `${CodeGenState.currentScope}_${localName}`
|
|
4173
4248
|
: localName;
|
|
4174
|
-
return { name, separator: "_"
|
|
4249
|
+
return { name, separator: "_" };
|
|
4175
4250
|
}
|
|
4176
4251
|
|
|
4177
4252
|
// Issue #478: Check for global types (global.Type)
|
|
@@ -4179,7 +4254,6 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4179
4254
|
return {
|
|
4180
4255
|
name: typeCtx.globalType()!.IDENTIFIER().getText(),
|
|
4181
4256
|
separator: "_",
|
|
4182
|
-
checkCppType: false,
|
|
4183
4257
|
};
|
|
4184
4258
|
}
|
|
4185
4259
|
|
|
@@ -4189,7 +4263,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4189
4263
|
const parts = typeCtx.qualifiedType()!.IDENTIFIER();
|
|
4190
4264
|
const name = this.resolveQualifiedType(parts.map((id) => id.getText()));
|
|
4191
4265
|
const separator = name.includes("::") ? "::" : "_";
|
|
4192
|
-
return { name, separator
|
|
4266
|
+
return { name, separator };
|
|
4193
4267
|
}
|
|
4194
4268
|
|
|
4195
4269
|
// Check for user-defined types (structs/classes/enums)
|
|
@@ -4197,28 +4271,12 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4197
4271
|
return {
|
|
4198
4272
|
name: typeCtx.userType()!.getText(),
|
|
4199
4273
|
separator: "_",
|
|
4200
|
-
checkCppType: true,
|
|
4201
4274
|
};
|
|
4202
4275
|
}
|
|
4203
4276
|
|
|
4204
4277
|
return null;
|
|
4205
4278
|
}
|
|
4206
4279
|
|
|
4207
|
-
/**
|
|
4208
|
-
* Check if a type needs empty brace initialization {}.
|
|
4209
|
-
* Issue #304: C++ types with constructors may fail with {0}
|
|
4210
|
-
* Issue #309: Unknown user types in C++ mode may have non-trivial constructors
|
|
4211
|
-
*/
|
|
4212
|
-
private _needsEmptyBraceInit(typeName: string): boolean {
|
|
4213
|
-
// C++ types (external libraries with constructors)
|
|
4214
|
-
if (this.isCppType(typeName)) {
|
|
4215
|
-
return true;
|
|
4216
|
-
}
|
|
4217
|
-
// In C++ mode, unknown user types may have non-trivial constructors
|
|
4218
|
-
// Known structs (C-Next or C headers) are POD types where {0} works
|
|
4219
|
-
return CodeGenState.cppMode && !this.isKnownStruct(typeName);
|
|
4220
|
-
}
|
|
4221
|
-
|
|
4222
4280
|
/**
|
|
4223
4281
|
* Generate a safe bit mask expression.
|
|
4224
4282
|
* Avoids undefined behavior when width >= 32 for 32-bit integers.
|
|
@@ -4750,8 +4808,7 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
4750
4808
|
// MISRA 10.3: Cast limit macros to target type (they have type 'int')
|
|
4751
4809
|
const finalCast = CppModeHelper.cast(targetType, `(${expr})`);
|
|
4752
4810
|
const castMax = CppModeHelper.cast(targetType, maxValue);
|
|
4753
|
-
const castMin =
|
|
4754
|
-
minValue === "0" ? "0" : CppModeHelper.cast(targetType, minValue);
|
|
4811
|
+
const castMin = CppModeHelper.cast(targetType, minValue);
|
|
4755
4812
|
return `((${expr}) > ${maxComparison} ? ${castMax} : (${expr}) < ${minComparison} ? ${castMin} : ${finalCast})`;
|
|
4756
4813
|
}
|
|
4757
4814
|
|