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.
Files changed (67) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +1403 -427
  3. package/dist/index.js.map +3 -3
  4. package/grammar/CNext.g4 +4 -0
  5. package/package.json +5 -1
  6. package/src/transpiler/Transpiler.ts +90 -22
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
  9. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  10. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  11. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  12. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  13. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  14. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
  15. package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
  16. package/src/transpiler/logic/symbols/SymbolTable.ts +54 -12
  17. package/src/transpiler/logic/symbols/SymbolUtils.ts +21 -8
  18. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +39 -4
  19. package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +2 -1
  20. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +2 -1
  21. package/src/transpiler/logic/symbols/cnext/__tests__/CNextResolver.integration.test.ts +5 -2
  22. package/src/transpiler/logic/symbols/cnext/__tests__/ScopeCollector.test.ts +5 -2
  23. package/src/transpiler/logic/symbols/cnext/collectors/ScopeCollector.ts +7 -2
  24. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  25. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  26. package/src/transpiler/logic/symbols/cpp/utils/DeclaratorUtils.ts +4 -2
  27. package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
  28. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +167 -18
  29. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
  30. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  31. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +32 -8
  33. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +91 -1
  34. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  35. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
  36. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  37. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  38. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  39. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  40. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  41. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
  42. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  43. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  44. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  45. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  46. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  47. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
  49. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  50. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  51. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  52. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  53. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  54. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  55. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
  56. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  57. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  58. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  59. package/src/transpiler/state/CodeGenState.ts +71 -6
  60. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  61. package/src/transpiler/types/symbols/c/ICFieldInfo.ts +6 -2
  62. package/src/transpiler/types/symbols/cpp/ICppFieldInfo.ts +5 -2
  63. package/src/utils/LiteralUtils.ts +23 -0
  64. package/src/utils/ScopeUtils.ts +19 -0
  65. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  66. package/src/utils/__tests__/ScopeUtils.test.ts +10 -0
  67. 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: Arrays need element type checking for C++ classes
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._getArrayZeroInitializer(typeCtx);
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
- // Check if C++ type needing {} (only for userType, not qualified/scoped/global)
1327
- if (resolved.checkCppType && this._needsEmptyBraceInit(resolved.name)) {
1328
- return "{}";
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.isPointer) {
1494
- // ADR-006: Non-array, non-callback parameters become pointers
1495
- // In C++ mode, use reference (&) instead of pointer (*)
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
- return this._resolveIdentifierExpression(ctx.IDENTIFIER()!.getText());
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 {} -> (Point){ 0 } or {} for C++ classes
3499
- return isCppClass ? "{}" : `(${castType}){ 0 }`;
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 `{ ${fieldInits.join(", ")} }`;
3624
+ return initializer;
3556
3625
  }
3557
3626
 
3558
- return `(${castType}){ ${fieldInits.join(", ")} }`;
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
- * Get zero initializer for array types.
4106
- * Issue #379: C++ class arrays must use {} instead of {0}
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 _getArrayZeroInitializer(typeCtx: Parser.TypeContext): string {
4109
- // Check if element type is a C++ class or template type
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, checkCppType } or null if not a named type.
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; checkCppType: boolean } | null {
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: "_", checkCppType: false };
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, checkCppType: false };
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: 10, y: 20};
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
- // The compound literal cast should include 'struct' keyword
676
- expect(code).toContain("(struct NamedPoint)");
677
- expect(code).toContain(".x = 10");
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 C mode", () => {
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: 1, y: 2};
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
- // Should NOT have 'struct' keyword since it's not marked
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: 5, y: 10};
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
- // In C++ mode, struct keyword should NOT be in the cast
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
  });