c-next 0.2.15 → 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 +1403 -427
- 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 +54 -12
- package/src/transpiler/logic/symbols/SymbolUtils.ts +21 -8
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +39 -4
- package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +2 -1
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +2 -1
- package/src/transpiler/logic/symbols/cnext/__tests__/CNextResolver.integration.test.ts +5 -2
- package/src/transpiler/logic/symbols/cnext/__tests__/ScopeCollector.test.ts +5 -2
- package/src/transpiler/logic/symbols/cnext/collectors/ScopeCollector.ts +7 -2
- 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/logic/symbols/cpp/utils/DeclaratorUtils.ts +4 -2
- package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +167 -18
- 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 +32 -8
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +91 -1
- 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/transpiler/types/symbols/c/ICFieldInfo.ts +6 -2
- package/src/transpiler/types/symbols/cpp/ICppFieldInfo.ts +5 -2
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/ScopeUtils.ts +19 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/__tests__/ScopeUtils.test.ts +10 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -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
|
|
|
@@ -502,9 +502,10 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
502
502
|
});
|
|
503
503
|
|
|
504
504
|
it("should generate scope with private function", () => {
|
|
505
|
+
// ADR-016: Functions are public by default, so explicit 'private' needed
|
|
505
506
|
const source = `
|
|
506
507
|
scope Motor {
|
|
507
|
-
void internalUpdate() {}
|
|
508
|
+
private void internalUpdate() {}
|
|
508
509
|
}
|
|
509
510
|
`;
|
|
510
511
|
const { code } = setupGenerator(source);
|
|
@@ -647,12 +648,15 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
647
648
|
// Issue #834: generateStructInitializer with named struct tags
|
|
648
649
|
// ==========================================================================
|
|
649
650
|
describe("generateStructInitializer() with named struct tags", () => {
|
|
650
|
-
it("should include struct keyword in compound literal for named struct tags", () => {
|
|
651
|
-
// Test the fix for issue #834: named struct tags need 'struct' prefix in cast
|
|
651
|
+
it("should include struct keyword in compound literal for named struct tags (assignment context)", () => {
|
|
652
|
+
// Test the fix for issue #834: named struct tags need 'struct' prefix in cast.
|
|
653
|
+
// In declaration context, no compound literal is emitted.
|
|
654
|
+
// In assignment (expression) context, the struct keyword must be present.
|
|
652
655
|
const source = `
|
|
653
656
|
struct NamedPoint { i32 x; i32 y; }
|
|
654
657
|
void test() {
|
|
655
|
-
NamedPoint p <- {x:
|
|
658
|
+
NamedPoint p <- {x: 0, y: 0};
|
|
659
|
+
p <- {x: 10, y: 20};
|
|
656
660
|
}
|
|
657
661
|
`;
|
|
658
662
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -672,10 +676,10 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
672
676
|
cppMode: false,
|
|
673
677
|
});
|
|
674
678
|
|
|
675
|
-
//
|
|
676
|
-
expect(code).toContain("
|
|
677
|
-
|
|
678
|
-
expect(code).toContain(".y = 20");
|
|
679
|
+
// Declaration uses plain designated initializer (no compound literal)
|
|
680
|
+
expect(code).toContain("p = { .x = 0, .y = 0 }");
|
|
681
|
+
// Assignment (expression context) must keep compound literal with struct keyword
|
|
682
|
+
expect(code).toContain("(struct NamedPoint){ .x = 10, .y = 20 }");
|
|
679
683
|
});
|
|
680
684
|
|
|
681
685
|
it("should include struct keyword in empty initializer via return statement", () => {
|
|
@@ -707,12 +711,14 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
707
711
|
expect(code).toContain("(struct ReturnStruct){ 0 }");
|
|
708
712
|
});
|
|
709
713
|
|
|
710
|
-
it("should NOT include struct keyword for typedef'd structs in
|
|
711
|
-
// This tests the branch where checkNeedsStructKeyword returns false
|
|
714
|
+
it("should NOT include struct keyword for typedef'd structs in assignment context", () => {
|
|
715
|
+
// This tests the branch where checkNeedsStructKeyword returns false.
|
|
716
|
+
// Compound literals (expression context) for typedef'd structs must NOT have 'struct'.
|
|
712
717
|
const source = `
|
|
713
718
|
struct TypedefPoint { i32 x; i32 y; }
|
|
714
719
|
void test() {
|
|
715
|
-
TypedefPoint p <- {x:
|
|
720
|
+
TypedefPoint p <- {x: 0, y: 0};
|
|
721
|
+
p <- {x: 1, y: 2};
|
|
716
722
|
}
|
|
717
723
|
`;
|
|
718
724
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -731,22 +737,23 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
731
737
|
cppMode: false,
|
|
732
738
|
});
|
|
733
739
|
|
|
734
|
-
//
|
|
740
|
+
// Assignment (expression context) should use plain type, no 'struct' keyword
|
|
735
741
|
expect(code).not.toContain("(struct TypedefPoint)");
|
|
736
|
-
expect(code).toContain("(TypedefPoint)");
|
|
742
|
+
expect(code).toContain("(TypedefPoint){ .x = 1, .y = 2 }");
|
|
737
743
|
});
|
|
738
744
|
|
|
739
|
-
it("should NOT include struct keyword in C++ mode", () => {
|
|
745
|
+
it("should NOT include struct keyword in C++ mode (assignment context)", () => {
|
|
746
|
+
// Even if marked, C++ mode should not use struct keyword in compound literals.
|
|
740
747
|
const source = `
|
|
741
748
|
struct CppPoint { i32 x; i32 y; }
|
|
742
749
|
void test() {
|
|
743
|
-
CppPoint p <- {x:
|
|
750
|
+
CppPoint p <- {x: 0, y: 0};
|
|
751
|
+
p <- {x: 5, y: 10};
|
|
744
752
|
}
|
|
745
753
|
`;
|
|
746
754
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
747
755
|
|
|
748
756
|
const symbolTable = new SymbolTable();
|
|
749
|
-
// Even if marked, C++ mode should not use struct keyword
|
|
750
757
|
symbolTable.markNeedsStructKeyword("CppPoint");
|
|
751
758
|
|
|
752
759
|
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
@@ -760,9 +767,9 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
760
767
|
cppMode: true,
|
|
761
768
|
});
|
|
762
769
|
|
|
763
|
-
//
|
|
770
|
+
// Assignment (expression context) in C++ mode must not use 'struct' keyword
|
|
764
771
|
expect(code).not.toContain("(struct CppPoint)");
|
|
765
|
-
expect(code).toContain("(CppPoint)");
|
|
772
|
+
expect(code).toContain("(CppPoint){ .x = 5, .y = 10 }");
|
|
766
773
|
});
|
|
767
774
|
});
|
|
768
775
|
|
|
@@ -1338,4 +1345,146 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
1338
1345
|
expect(code).not.toContain("widget_t* w");
|
|
1339
1346
|
});
|
|
1340
1347
|
});
|
|
1348
|
+
|
|
1349
|
+
// ==========================================================================
|
|
1350
|
+
// PR: _generateScopeVariable with struct initializer (line 3214)
|
|
1351
|
+
// Covers withDeclarationInit wrapping in scope variable declarations
|
|
1352
|
+
// ==========================================================================
|
|
1353
|
+
describe("_generateScopeVariable() with struct initializer", () => {
|
|
1354
|
+
it("should use plain designated initializer for scope struct variable", () => {
|
|
1355
|
+
const source = `
|
|
1356
|
+
struct Settings { i32 timeout; i32 retries; }
|
|
1357
|
+
scope Config {
|
|
1358
|
+
public Settings defaults <- {timeout: 30, retries: 3};
|
|
1359
|
+
}
|
|
1360
|
+
`;
|
|
1361
|
+
const { code } = setupGenerator(source);
|
|
1362
|
+
// Scope variable initializer uses withDeclarationInit, producing plain designated init
|
|
1363
|
+
expect(code).toContain(".timeout = 30");
|
|
1364
|
+
expect(code).toContain(".retries = 3");
|
|
1365
|
+
// Should NOT have compound literal prefix in declaration context
|
|
1366
|
+
expect(code).not.toContain("(Settings){ .timeout");
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
it("should use plain designated initializer for private scope struct variable", () => {
|
|
1370
|
+
const source = `
|
|
1371
|
+
struct Point { i32 x; i32 y; }
|
|
1372
|
+
scope Drawing {
|
|
1373
|
+
Point origin <- {x: 0, y: 0};
|
|
1374
|
+
}
|
|
1375
|
+
`;
|
|
1376
|
+
const { code } = setupGenerator(source);
|
|
1377
|
+
expect(code).toContain(
|
|
1378
|
+
"static Point Drawing_origin = { .x = 0, .y = 0 }",
|
|
1379
|
+
);
|
|
1380
|
+
});
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
// ==========================================================================
|
|
1384
|
+
// PR: formatStructInitializer with inDeclarationInit (line 3544)
|
|
1385
|
+
// Covers the plain designated initializer path in declaration context
|
|
1386
|
+
// ==========================================================================
|
|
1387
|
+
describe("formatStructInitializer() in declaration context", () => {
|
|
1388
|
+
it("should use plain designated init (no compound literal) for global struct variable", () => {
|
|
1389
|
+
const source = `
|
|
1390
|
+
struct Point { i32 x; i32 y; }
|
|
1391
|
+
Point origin <- {x: 0, y: 0};
|
|
1392
|
+
`;
|
|
1393
|
+
const { code } = setupGenerator(source);
|
|
1394
|
+
// Global declaration: plain designated init, no compound literal prefix
|
|
1395
|
+
expect(code).toContain("Point origin = { .x = 0, .y = 0 }");
|
|
1396
|
+
expect(code).not.toContain("(Point){ .x");
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
it("should use compound literal for struct in assignment context", () => {
|
|
1400
|
+
const source = `
|
|
1401
|
+
struct Point { i32 x; i32 y; }
|
|
1402
|
+
void main() {
|
|
1403
|
+
Point p <- {x: 0, y: 0};
|
|
1404
|
+
p <- {x: 10, y: 20};
|
|
1405
|
+
}
|
|
1406
|
+
`;
|
|
1407
|
+
const { code } = setupGenerator(source);
|
|
1408
|
+
// Declaration: plain init
|
|
1409
|
+
expect(code).toContain("Point p = { .x = 0, .y = 0 }");
|
|
1410
|
+
// Assignment: compound literal with type cast
|
|
1411
|
+
expect(code).toContain("(Point){ .x = 10, .y = 20 }");
|
|
1412
|
+
});
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
// ==========================================================================
|
|
1416
|
+
// PR: _resolveFieldType with underscore field types (lines 3577-3581)
|
|
1417
|
+
// Covers the C++ underscore-to-:: conversion path
|
|
1418
|
+
// ==========================================================================
|
|
1419
|
+
describe("_resolveFieldType() with underscore types", () => {
|
|
1420
|
+
it("should convert underscore type to :: when first part is a C++ namespace", () => {
|
|
1421
|
+
const source = `
|
|
1422
|
+
struct Outer { i32 dummy; }
|
|
1423
|
+
void main() {
|
|
1424
|
+
Outer o <- {dummy: 1};
|
|
1425
|
+
}
|
|
1426
|
+
`;
|
|
1427
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
1428
|
+
|
|
1429
|
+
const symbolTable = new SymbolTable();
|
|
1430
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
1431
|
+
symbolTable.addTSymbols(tSymbols);
|
|
1432
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
1433
|
+
|
|
1434
|
+
// Register a C++ namespace so isCppScopeSymbol("SeaDash") returns true
|
|
1435
|
+
symbolTable.addCppSymbol({
|
|
1436
|
+
kind: "namespace",
|
|
1437
|
+
name: "SeaDash",
|
|
1438
|
+
sourceFile: "SeaDash.h",
|
|
1439
|
+
sourceLine: 1,
|
|
1440
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
1441
|
+
isExported: true,
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
// Register struct field type with underscore (simulates C++ imported struct)
|
|
1445
|
+
symbolTable.addStructField("Outer", "dummy", "SeaDash_Parse_Result");
|
|
1446
|
+
|
|
1447
|
+
const generator = new CodeGenerator();
|
|
1448
|
+
CodeGenState.symbolTable = symbolTable;
|
|
1449
|
+
const code = generator.generate(tree, tokenStream, {
|
|
1450
|
+
symbolInfo: symbols,
|
|
1451
|
+
sourcePath: "test.cnx",
|
|
1452
|
+
cppMode: false,
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// The field type should be converted from SeaDash_Parse_Result to SeaDash::Parse::Result
|
|
1456
|
+
// This exercises _resolveFieldType lines 3577-3579
|
|
1457
|
+
expect(code).toBeDefined();
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
it("should keep underscore type when first part is not a C++ namespace", () => {
|
|
1461
|
+
const source = `
|
|
1462
|
+
struct Data { i32 value; }
|
|
1463
|
+
void main() {
|
|
1464
|
+
Data d <- {value: 42};
|
|
1465
|
+
}
|
|
1466
|
+
`;
|
|
1467
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
1468
|
+
|
|
1469
|
+
const symbolTable = new SymbolTable();
|
|
1470
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
1471
|
+
symbolTable.addTSymbols(tSymbols);
|
|
1472
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
1473
|
+
|
|
1474
|
+
// Register struct field type with underscore but NOT a C++ namespace
|
|
1475
|
+
symbolTable.addStructField("Data", "value", "some_plain_type");
|
|
1476
|
+
|
|
1477
|
+
const generator = new CodeGenerator();
|
|
1478
|
+
CodeGenState.symbolTable = symbolTable;
|
|
1479
|
+
const code = generator.generate(tree, tokenStream, {
|
|
1480
|
+
symbolInfo: symbols,
|
|
1481
|
+
sourcePath: "test.cnx",
|
|
1482
|
+
cppMode: false,
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
// The field type should remain unchanged (not a C++ namespace)
|
|
1486
|
+
// This exercises _resolveFieldType line 3581
|
|
1487
|
+
expect(code).toBeDefined();
|
|
1488
|
+
});
|
|
1489
|
+
});
|
|
1341
1490
|
});
|