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
@@ -1504,7 +1504,7 @@ describe("CodeGenerator", () => {
1504
1504
 
1505
1505
  it("should generate multi-dimensional array", () => {
1506
1506
  const source = `
1507
- u32[3] matrix[3];
1507
+ u32[3][3] matrix;
1508
1508
  void main() { }
1509
1509
  `;
1510
1510
  const { tree, tokenStream } = CNextSourceParser.parse(source);
@@ -2944,7 +2944,7 @@ describe("CodeGenerator", () => {
2944
2944
  describe("Inferred array size", () => {
2945
2945
  it("should generate array with inferred size from initializer", () => {
2946
2946
  const source = `
2947
- u32 values[] <- [1, 2, 3, 4, 5];
2947
+ u32[] values <- [1, 2, 3, 4, 5];
2948
2948
  void main() { }
2949
2949
  `;
2950
2950
  const { tree, tokenStream } = CNextSourceParser.parse(source);
@@ -3799,7 +3799,7 @@ describe("CodeGenerator", () => {
3799
3799
  describe("Nested array initializers", () => {
3800
3800
  it("should generate 2D array initializer", () => {
3801
3801
  const source = `
3802
- u32[2] matrix[3] <- [[1, 2, 3], [4, 5, 6]];
3802
+ u32[2][3] matrix <- [[1, 2, 3], [4, 5, 6]];
3803
3803
  `;
3804
3804
  const { tree, tokenStream } = CNextSourceParser.parse(source);
3805
3805
  const generator = new CodeGenerator();
@@ -4097,7 +4097,7 @@ describe("CodeGenerator", () => {
4097
4097
  describe("Multi-dimensional array", () => {
4098
4098
  it("should generate 3D array declaration", () => {
4099
4099
  const source = `
4100
- u8[2] cube[3][4];
4100
+ u8[2][3][4] cube;
4101
4101
  `;
4102
4102
  const { tree, tokenStream } = CNextSourceParser.parse(source);
4103
4103
  const generator = new CodeGenerator();
@@ -6932,7 +6932,7 @@ describe("CodeGenerator", () => {
6932
6932
  expect(code).toContain("ExternalClass obj = {}");
6933
6933
  });
6934
6934
 
6935
- it("should initialize known structs to {0} even in C++ mode", () => {
6935
+ it("should initialize known structs to {} in C++ mode (Issue #1004)", () => {
6936
6936
  const source = `
6937
6937
  struct Point { i32 x; i32 y; }
6938
6938
  void main() {
@@ -6950,8 +6950,10 @@ describe("CodeGenerator", () => {
6950
6950
  cppMode: true,
6951
6951
  });
6952
6952
 
6953
- // Known C-Next structs are POD types, {0} works fine
6954
- expect(code).toContain("Point p = {0}");
6953
+ // Issue #1004: C++ value-initialization ({}) is valid for any struct,
6954
+ // including ones whose first field is an enum where {0} would be an
6955
+ // invalid int->enum narrowing.
6956
+ expect(code).toContain("Point p = {}");
6955
6957
  });
6956
6958
 
6957
6959
  // Note: Template type tests skipped - template argument transformation
@@ -7023,7 +7025,7 @@ describe("CodeGenerator", () => {
7023
7025
  expect(code).not.toContain("uint8_t* buf");
7024
7026
  });
7025
7027
 
7026
- it("should generate primitive C-Next style arrays with {0} in C++ mode", () => {
7028
+ it("should generate primitive C-Next style arrays with {} in C++ mode (Issue #1004)", () => {
7027
7029
  const source = `
7028
7030
  void main() {
7029
7031
  u32[8] counters;
@@ -7040,8 +7042,9 @@ describe("CodeGenerator", () => {
7040
7042
  cppMode: true,
7041
7043
  });
7042
7044
 
7043
- // Primitive type arrays should still use {0}
7044
- expect(code).toContain("uint32_t counters[8] = {0}");
7045
+ // Issue #1004: C++ arrays value-initialize with {} regardless of
7046
+ // element type (one unified aggregate zero-init rule).
7047
+ expect(code).toContain("uint32_t counters[8] = {}");
7045
7048
  });
7046
7049
 
7047
7050
  it("should generate unknown user type C-Next style arrays with {} in C++ mode", () => {
@@ -7065,7 +7068,7 @@ describe("CodeGenerator", () => {
7065
7068
  expect(code).toContain("UnknownType items[4] = {}");
7066
7069
  });
7067
7070
 
7068
- it("should generate known C-Next struct arrays with {0} in C++ mode", () => {
7071
+ it("should generate known C-Next struct arrays with {} in C++ mode (Issue #1004)", () => {
7069
7072
  const source = `
7070
7073
  struct Point { i32 x; i32 y; }
7071
7074
  void main() {
@@ -7083,8 +7086,9 @@ describe("CodeGenerator", () => {
7083
7086
  cppMode: true,
7084
7087
  });
7085
7088
 
7086
- // Known C-Next structs are POD types, {0} is correct
7087
- expect(code).toContain("Point pts[3] = {0}");
7089
+ // Issue #1004: C++ struct arrays value-initialize with {}, valid even
7090
+ // when the struct's first field is an enum.
7091
+ expect(code).toContain("Point pts[3] = {}");
7088
7092
  });
7089
7093
  });
7090
7094
  });
@@ -8483,7 +8487,7 @@ describe("CodeGenerator", () => {
8483
8487
  it("should handle multi-dimensional array access", () => {
8484
8488
  const source = `
8485
8489
  void test() {
8486
- u32[3] matrix[3];
8490
+ u32[3][3] matrix;
8487
8491
  u32 val <- matrix[1][2];
8488
8492
  }
8489
8493
  `;
@@ -9420,7 +9424,9 @@ describe("CodeGenerator", () => {
9420
9424
  sourcePath: "test.cnx",
9421
9425
  });
9422
9426
 
9423
- expect(code).toContain("(x > 3U) ? 10U : 20U");
9427
+ // Issue #1032: Comparison operands don't get U suffix (would change semantics)
9428
+ // but ternary arms do (they're assigned to u32 result)
9429
+ expect(code).toContain("(x > 3) ? 10U : 20U");
9424
9430
  });
9425
9431
  });
9426
9432
 
@@ -11293,7 +11299,7 @@ describe("CodeGenerator", () => {
11293
11299
  it("should allow empty brackets for size inference", () => {
11294
11300
  const source = `
11295
11301
  void test() {
11296
- u8 data[] <- [1, 2, 3];
11302
+ u8[] data <- [1, 2, 3];
11297
11303
  }
11298
11304
  `;
11299
11305
  const { tree, tokenStream } = CNextSourceParser.parse(source);
@@ -11309,7 +11315,7 @@ describe("CodeGenerator", () => {
11309
11315
  expect(code).toContain("uint8_t data[3]");
11310
11316
  });
11311
11317
 
11312
- it("should allow multi-dimensional C-style arrays", () => {
11318
+ it("should reject multi-dimensional C-style arrays (Issue #1014)", () => {
11313
11319
  const source = `
11314
11320
  void test() {
11315
11321
  u8 matrix[4][4];
@@ -11321,6 +11327,26 @@ describe("CodeGenerator", () => {
11321
11327
  const tSymbols = CNextResolver.resolve(tree, "test.cnx");
11322
11328
  const symbols = TSymbolInfoAdapter.convert(tSymbols);
11323
11329
 
11330
+ expect(() =>
11331
+ generator.generate(tree, tokenStream, {
11332
+ symbolInfo: symbols,
11333
+ sourcePath: "test.cnx",
11334
+ }),
11335
+ ).toThrow("C-style array declaration is not allowed");
11336
+ });
11337
+
11338
+ it("should allow multi-dimensional C-Next style arrays", () => {
11339
+ const source = `
11340
+ void test() {
11341
+ u8[4][4] matrix;
11342
+ matrix[0][0] <- 0;
11343
+ }
11344
+ `;
11345
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
11346
+ const generator = new CodeGenerator();
11347
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
11348
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
11349
+
11324
11350
  const code = generator.generate(tree, tokenStream, {
11325
11351
  symbolInfo: symbols,
11326
11352
  sourcePath: "test.cnx",
@@ -14042,7 +14068,7 @@ describe("CodeGenerator", () => {
14042
14068
  it("should detect array element of string array", () => {
14043
14069
  const source = `
14044
14070
  void test() {
14045
- string<32> names[3];
14071
+ string<32>[3] names;
14046
14072
  names[0] <- "Alice";
14047
14073
  bool check <- (names[0] = "Alice");
14048
14074
  }
@@ -14086,7 +14112,7 @@ describe("CodeGenerator", () => {
14086
14112
  it("should register multi-dimensional array", () => {
14087
14113
  const source = `
14088
14114
  void test() {
14089
- u8[10] matrix[20];
14115
+ u8[10][20] matrix;
14090
14116
  matrix[0][0] <- 0;
14091
14117
  }
14092
14118
  `;
@@ -14892,8 +14918,8 @@ describe("CodeGenerator", () => {
14892
14918
  });
14893
14919
  });
14894
14920
 
14895
- describe("break and continue statements", () => {
14896
- it("should generate break in loop", () => {
14921
+ describe("break and continue rejection (Issue #1011)", () => {
14922
+ it("should reject break - not part of C-Next spec", () => {
14897
14923
  const source = `
14898
14924
  void test() {
14899
14925
  while (true) {
@@ -14906,15 +14932,15 @@ describe("CodeGenerator", () => {
14906
14932
  const tSymbols = CNextResolver.resolve(tree, "test.cnx");
14907
14933
  const symbols = TSymbolInfoAdapter.convert(tSymbols);
14908
14934
 
14909
- const code = generator.generate(tree, tokenStream, {
14910
- symbolInfo: symbols,
14911
- sourcePath: "test.cnx",
14912
- });
14913
-
14914
- expect(code).toContain("break;");
14935
+ expect(() =>
14936
+ generator.generate(tree, tokenStream, {
14937
+ symbolInfo: symbols,
14938
+ sourcePath: "test.cnx",
14939
+ }),
14940
+ ).toThrow("'break' is not supported in C-Next");
14915
14941
  });
14916
14942
 
14917
- it("should generate continue in loop", () => {
14943
+ it("should reject continue - not part of C-Next spec", () => {
14918
14944
  const source = `
14919
14945
  void test() {
14920
14946
  u32 i <- 0;
@@ -14931,12 +14957,12 @@ describe("CodeGenerator", () => {
14931
14957
  const tSymbols = CNextResolver.resolve(tree, "test.cnx");
14932
14958
  const symbols = TSymbolInfoAdapter.convert(tSymbols);
14933
14959
 
14934
- const code = generator.generate(tree, tokenStream, {
14935
- symbolInfo: symbols,
14936
- sourcePath: "test.cnx",
14937
- });
14938
-
14939
- expect(code).toContain("continue;");
14960
+ expect(() =>
14961
+ generator.generate(tree, tokenStream, {
14962
+ symbolInfo: symbols,
14963
+ sourcePath: "test.cnx",
14964
+ }),
14965
+ ).toThrow("'continue' is not supported in C-Next");
14940
14966
  });
14941
14967
  });
14942
14968
 
@@ -15632,4 +15658,94 @@ describe("CodeGenerator", () => {
15632
15658
  }).toThrow("Error: enum generator not registered");
15633
15659
  });
15634
15660
  });
15661
+
15662
+ describe("struct initializer — declaration vs expression context", () => {
15663
+ it("should use designated initializer without type cast for global struct declaration", () => {
15664
+ const source = `
15665
+ struct Point { i32 x; i32 y; }
15666
+ Point origin <- {x: 0, y: 0};
15667
+ `;
15668
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
15669
+ const generator = new CodeGenerator();
15670
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
15671
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
15672
+
15673
+ const code = generator.generate(tree, tokenStream, {
15674
+ symbolInfo: symbols,
15675
+ sourcePath: "test.cnx",
15676
+ });
15677
+
15678
+ // Must use plain designated initializer — (Point){...} is not a C99 constant expression
15679
+ // and fails to compile at file scope on GCC < 13
15680
+ expect(code).toContain("origin = { .x = 0, .y = 0 }");
15681
+ expect(code).not.toContain("(Point)");
15682
+ });
15683
+
15684
+ it("should use designated initializer without type cast for local struct declaration", () => {
15685
+ const source = `
15686
+ struct Point { i32 x; i32 y; }
15687
+ void foo() {
15688
+ Point p <- {x: 10, y: 20};
15689
+ }
15690
+ `;
15691
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
15692
+ const generator = new CodeGenerator();
15693
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
15694
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
15695
+
15696
+ const code = generator.generate(tree, tokenStream, {
15697
+ symbolInfo: symbols,
15698
+ sourcePath: "test.cnx",
15699
+ });
15700
+
15701
+ expect(code).toContain("p = { .x = 10, .y = 20 }");
15702
+ expect(code).not.toContain("(Point)");
15703
+ });
15704
+
15705
+ it("should use designated initializer for nested struct literals in declaration", () => {
15706
+ const source = `
15707
+ struct Point { i32 x; i32 y; }
15708
+ struct Line { Point start; Point end; }
15709
+ Line seg <- {start: {x: 0, y: 0}, end: {x: 100, y: 100}};
15710
+ `;
15711
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
15712
+ const generator = new CodeGenerator();
15713
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
15714
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
15715
+
15716
+ const code = generator.generate(tree, tokenStream, {
15717
+ symbolInfo: symbols,
15718
+ sourcePath: "test.cnx",
15719
+ });
15720
+
15721
+ // Neither outer nor inner initializer should have a type cast prefix
15722
+ expect(code).toContain(
15723
+ "{ .start = { .x = 0, .y = 0 }, .end = { .x = 100, .y = 100 } }",
15724
+ );
15725
+ expect(code).not.toContain("(Line)");
15726
+ expect(code).not.toContain("(Point)");
15727
+ });
15728
+
15729
+ it("should keep compound literal type cast for struct re-assignment (expression context)", () => {
15730
+ const source = `
15731
+ struct Point { i32 x; i32 y; }
15732
+ void foo() {
15733
+ Point p <- {x: 0, y: 0};
15734
+ p <- {x: 10, y: 20};
15735
+ }
15736
+ `;
15737
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
15738
+ const generator = new CodeGenerator();
15739
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
15740
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
15741
+
15742
+ const code = generator.generate(tree, tokenStream, {
15743
+ symbolInfo: symbols,
15744
+ sourcePath: "test.cnx",
15745
+ });
15746
+
15747
+ // Re-assignment must use compound literal — plain { } is not valid as an expression
15748
+ expect(code).toContain("p = (Point){ .x = 10, .y = 20 }");
15749
+ });
15750
+ });
15635
15751
  });
@@ -27,7 +27,7 @@ describe("trackVariableTypeWithName helpers", () => {
27
27
  describe("extractArrayDimensionsSimple", () => {
28
28
  it("handles string array with single dimension", async () => {
29
29
  const source = `
30
- string<32> messages[4];
30
+ string<32>[4] messages;
31
31
  void main() {
32
32
  messages[0] <- "Hello";
33
33
  }
@@ -39,7 +39,7 @@ describe("trackVariableTypeWithName helpers", () => {
39
39
 
40
40
  it("handles string array with multiple dimensions", async () => {
41
41
  const source = `
42
- string<16> grid[2][3];
42
+ string<16>[2][3] grid;
43
43
  void main() {
44
44
  grid[0][0] <- "test";
45
45
  }
@@ -650,6 +650,7 @@ describe("TypeValidator", () => {
650
650
  type: "int",
651
651
  isConst: false,
652
652
  isPointer: false,
653
+ isStruct: false,
653
654
  isArray: false,
654
655
  arrayDims: "",
655
656
  },
@@ -665,6 +666,7 @@ describe("TypeValidator", () => {
665
666
  type: "int",
666
667
  isConst: false,
667
668
  isPointer: false,
669
+ isStruct: false,
668
670
  isArray: false,
669
671
  arrayDims: "",
670
672
  },
@@ -702,6 +704,7 @@ describe("TypeValidator", () => {
702
704
  type: "int",
703
705
  isConst: false,
704
706
  isPointer: false,
707
+ isStruct: false,
705
708
  isArray: false,
706
709
  arrayDims: "",
707
710
  },
@@ -728,6 +731,7 @@ describe("TypeValidator", () => {
728
731
  type: "int",
729
732
  isConst: false,
730
733
  isPointer: false,
734
+ isStruct: false,
731
735
  isArray: false,
732
736
  arrayDims: "",
733
737
  },
@@ -743,6 +747,7 @@ describe("TypeValidator", () => {
743
747
  type: "float",
744
748
  isConst: false,
745
749
  isPointer: false,
750
+ isStruct: false,
746
751
  isArray: false,
747
752
  arrayDims: "",
748
753
  },
@@ -763,6 +768,7 @@ describe("TypeValidator", () => {
763
768
  type: "int",
764
769
  isConst: true,
765
770
  isPointer: false,
771
+ isStruct: false,
766
772
  isArray: false,
767
773
  arrayDims: "",
768
774
  },
@@ -778,6 +784,7 @@ describe("TypeValidator", () => {
778
784
  type: "int",
779
785
  isConst: false,
780
786
  isPointer: false,
787
+ isStruct: false,
781
788
  isArray: false,
782
789
  arrayDims: "",
783
790
  },
@@ -798,6 +805,7 @@ describe("TypeValidator", () => {
798
805
  type: "int",
799
806
  isConst: false,
800
807
  isPointer: true,
808
+ isStruct: false,
801
809
  isArray: false,
802
810
  arrayDims: "",
803
811
  },
@@ -813,6 +821,7 @@ describe("TypeValidator", () => {
813
821
  type: "int",
814
822
  isConst: false,
815
823
  isPointer: false,
824
+ isStruct: false,
816
825
  isArray: false,
817
826
  arrayDims: "",
818
827
  },
@@ -833,6 +842,7 @@ describe("TypeValidator", () => {
833
842
  type: "int",
834
843
  isConst: false,
835
844
  isPointer: false,
845
+ isStruct: false,
836
846
  isArray: true,
837
847
  arrayDims: "[10]",
838
848
  },
@@ -848,6 +858,7 @@ describe("TypeValidator", () => {
848
858
  type: "int",
849
859
  isConst: false,
850
860
  isPointer: false,
861
+ isStruct: false,
851
862
  isArray: false,
852
863
  arrayDims: "",
853
864
  },
@@ -25,6 +25,8 @@ import BitmapCommentUtils from "./BitmapCommentUtils";
25
25
  import ArrayDimensionUtils from "./ArrayDimensionUtils";
26
26
  import QualifiedNameGenerator from "../../utils/QualifiedNameGenerator";
27
27
  import CodeGenState from "../../../../state/CodeGenState";
28
+ import ScopeUtils from "../../../../../utils/ScopeUtils";
29
+ import VariableModifierBuilder from "../../helpers/VariableModifierBuilder";
28
30
 
29
31
  /**
30
32
  * Generate initializer expression for a variable declaration.
@@ -37,10 +39,12 @@ function generateInitializer(
37
39
  ): string {
38
40
  if (varDecl.expression()) {
39
41
  // Issue #872: Set expectedType for MISRA 7.2 U suffix compliance
42
+ // Issue #992: withDeclarationInit suppresses compound literals at file scope (GCC 9-12 compat)
40
43
  const typeName = orchestrator.generateType(varDecl.type());
41
- return CodeGenState.withExpectedType(
42
- typeName,
43
- () => ` = ${orchestrator.generateExpression(varDecl.expression()!)}`,
44
+ return CodeGenState.withExpectedType(typeName, () =>
45
+ CodeGenState.withDeclarationInit(
46
+ () => ` = ${orchestrator.generateExpression(varDecl.expression()!)}`,
47
+ ),
44
48
  );
45
49
  }
46
50
  // ADR-015: Zero initialization for uninitialized scope variables
@@ -203,12 +207,23 @@ function generateRegularVariable(
203
207
  orchestrator.markOpaqueScopeVariable(fullName);
204
208
  }
205
209
 
206
- // Issue #282: Add 'const' modifier for const variables
210
+ // Issue #998: Use VariableModifierBuilder for consistent modifier handling
211
+ // This handles const, atomic, volatile, and validates mutual exclusion
212
+ // Scope variables are always at file scope (not in function body), no initializer for modifier purposes
213
+ const modifiers = VariableModifierBuilder.build(
214
+ varDecl,
215
+ false, // inFunctionBody - scope vars are file scope
216
+ false, // hasInitializer - doesn't affect volatile/atomic handling
217
+ false, // cppMode - doesn't affect volatile/atomic handling
218
+ );
219
+ // For scope variables: static for private, no modifier for public
220
+ // Then add volatile (from atomic or volatile keyword), then const
221
+ const staticPrefix = isPrivate ? "static " : "";
222
+ const volatilePrefix = modifiers.atomic || modifiers.volatile;
207
223
  const constPrefix = isConst ? "const " : "";
208
- const prefix = isPrivate ? "static " : "";
209
224
 
210
225
  // Build declaration with all dimensions
211
- let decl = `${prefix}${constPrefix}${type} ${fullName}`;
226
+ let decl = `${staticPrefix}${volatilePrefix}${constPrefix}${type} ${fullName}`;
212
227
  decl += ArrayDimensionUtils.generateArrayTypeDimension(
213
228
  arrayTypeCtx,
214
229
  orchestrator,
@@ -224,7 +239,12 @@ function generateRegularVariable(
224
239
 
225
240
  // Issue #948: Opaque types use NULL initialization instead of {0}
226
241
  // Issue #958: External typedef struct types also use NULL initialization
227
- if (isOpaque || isExternalStruct) {
242
+ // Issue #996: ...but only for SCALAR handles, which are single pointers. An
243
+ // *array* of opaque handles needs a brace initializer ({0}), not a scalar
244
+ // NULL. Route arrays through generateInitializer, which uses
245
+ // getZeroInitializer(type, isArray) — the single source of truth for
246
+ // zero-initialization (ADR-015).
247
+ if ((isOpaque || isExternalStruct) && !isArray) {
228
248
  decl += " = NULL";
229
249
  } else {
230
250
  decl += generateInitializer(varDecl, isArray, orchestrator);
@@ -338,7 +358,11 @@ function processScopeMember(
338
358
  state: IGeneratorState,
339
359
  orchestrator: IOrchestrator,
340
360
  ): string[] {
341
- const visibility = member.visibilityModifier()?.getText() || "private";
361
+ // ADR-016: Member-type-aware visibility defaults via ScopeUtils
362
+ const explicitVisibility = member.visibilityModifier()?.getText();
363
+ const isFunction = member.functionDeclaration() !== null;
364
+ const visibility =
365
+ explicitVisibility ?? ScopeUtils.getDefaultVisibility(isFunction);
342
366
  const isPrivate = visibility === "private";
343
367
 
344
368
  // Handle variable declarations
@@ -126,6 +126,8 @@ function createMockVariableDecl(options: {
126
126
  name: string;
127
127
  type: string;
128
128
  isConst?: boolean;
129
+ isAtomic?: boolean;
130
+ isVolatile?: boolean;
129
131
  initialValue?: string;
130
132
  arrayDims?: string[];
131
133
  hasStringType?: boolean;
@@ -142,6 +144,10 @@ function createMockVariableDecl(options: {
142
144
  options.arrayTypeSize,
143
145
  ),
144
146
  constModifier: () => createMockConstModifier(options.isConst ?? false),
147
+ // Issue #998: atomic and volatile modifiers for scope variables
148
+ // Uses VariableModifierBuilder for consistent handling
149
+ atomicModifier: () => (options.isAtomic ? ({} as unknown) : null),
150
+ volatileModifier: () => (options.isVolatile ? ({} as unknown) : null),
145
151
  expression: () =>
146
152
  options.initialValue ? createMockExpression(options.initialValue) : null,
147
153
  arrayDimension: () =>
@@ -772,6 +778,86 @@ describe("ScopeGenerator", () => {
772
778
  expect(result.code).toContain("static Point Canvas_point = 0;");
773
779
  expect(result.code).not.toContain("Point*");
774
780
  });
781
+
782
+ it("generates atomic variable with volatile modifier (Issue #998)", () => {
783
+ const varDecl = createMockVariableDecl({
784
+ name: "counter",
785
+ type: "u32",
786
+ isAtomic: true,
787
+ initialValue: "0",
788
+ });
789
+ const member = createMockScopeMember({ variableDecl: varDecl });
790
+ const ctx = createMockScopeContext("ISR", [member]);
791
+ const input = createMockInput();
792
+ const state = createMockState();
793
+ const orchestrator = createMockOrchestrator();
794
+
795
+ const result = generateScope(ctx, input, state, orchestrator);
796
+
797
+ expect(result.code).toContain(
798
+ "static volatile uint32_t ISR_counter = 0;",
799
+ );
800
+ });
801
+
802
+ it("generates public atomic variable with volatile but without static (Issue #998)", () => {
803
+ const varDecl = createMockVariableDecl({
804
+ name: "flag",
805
+ type: "bool",
806
+ isAtomic: true,
807
+ initialValue: "false",
808
+ });
809
+ const member = createMockScopeMember({
810
+ visibility: "public",
811
+ variableDecl: varDecl,
812
+ });
813
+ const ctx = createMockScopeContext("Shared", [member]);
814
+ const input = createMockInput();
815
+ const state = createMockState();
816
+ const orchestrator = createMockOrchestrator();
817
+
818
+ const result = generateScope(ctx, input, state, orchestrator);
819
+
820
+ expect(result.code).toContain("volatile bool Shared_flag = false;");
821
+ expect(result.code).not.toContain("static volatile bool Shared_flag");
822
+ });
823
+
824
+ it("generates volatile-keyword variable with volatile modifier (Issue #998)", () => {
825
+ const varDecl = createMockVariableDecl({
826
+ name: "reg",
827
+ type: "u8",
828
+ isVolatile: true,
829
+ initialValue: "0",
830
+ });
831
+ const member = createMockScopeMember({ variableDecl: varDecl });
832
+ const ctx = createMockScopeContext("HW", [member]);
833
+ const input = createMockInput();
834
+ const state = createMockState();
835
+ const orchestrator = createMockOrchestrator();
836
+
837
+ const result = generateScope(ctx, input, state, orchestrator);
838
+
839
+ expect(result.code).toContain("static volatile uint8_t HW_reg = 0;");
840
+ });
841
+
842
+ it("throws error when both atomic and volatile are specified (Issue #998)", () => {
843
+ const varDecl = createMockVariableDecl({
844
+ name: "bad",
845
+ type: "u8",
846
+ isAtomic: true,
847
+ isVolatile: true,
848
+ initialValue: "0",
849
+ startLine: 10,
850
+ });
851
+ const member = createMockScopeMember({ variableDecl: varDecl });
852
+ const ctx = createMockScopeContext("Test", [member]);
853
+ const input = createMockInput();
854
+ const state = createMockState();
855
+ const orchestrator = createMockOrchestrator();
856
+
857
+ expect(() => generateScope(ctx, input, state, orchestrator)).toThrow(
858
+ /Cannot use both 'atomic' and 'volatile' modifiers/,
859
+ );
860
+ });
775
861
  });
776
862
 
777
863
  // ========================================================================
@@ -857,7 +943,11 @@ describe("ScopeGenerator", () => {
857
943
  name: "helper",
858
944
  returnType: "void",
859
945
  });
860
- const member = createMockScopeMember({ functionDecl: funcDecl });
946
+ // ADR-016: Functions are public by default, so explicit 'private' needed
947
+ const member = createMockScopeMember({
948
+ visibility: "private",
949
+ functionDecl: funcDecl,
950
+ });
861
951
  const ctx = createMockScopeContext("Utils", [member]);
862
952
  const input = createMockInput();
863
953
  const state = createMockState();