c-next 0.2.16 → 0.2.18

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 (118) hide show
  1. package/README.md +18 -2
  2. package/dist/index.js +8897 -6260
  3. package/dist/index.js.map +4 -4
  4. package/grammar/CNext.g4 +12 -0
  5. package/package.json +4 -2
  6. package/src/transpiler/Transpiler.ts +376 -48
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
  9. package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
  10. package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
  11. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
  12. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  13. package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
  14. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
  15. package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
  16. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  17. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  18. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  19. package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
  20. package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
  21. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  22. package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
  23. package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
  24. package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
  25. package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
  26. package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
  27. package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
  28. package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
  29. package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
  30. package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
  31. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  32. package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
  33. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  34. package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
  35. package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
  36. package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
  37. package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
  38. package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
  39. package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
  40. package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
  41. package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
  42. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
  43. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
  44. package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
  45. package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
  46. package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
  47. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
  48. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  49. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  50. package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
  51. package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
  52. package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
  53. package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
  54. package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
  55. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  56. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
  57. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  58. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
  59. package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
  60. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
  61. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
  62. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
  63. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
  64. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
  65. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
  66. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
  67. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
  68. package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
  69. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
  70. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
  71. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
  72. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
  73. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
  74. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  75. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  76. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  77. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
  78. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  79. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
  80. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  81. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  82. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
  83. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
  84. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
  85. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
  86. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  87. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  88. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
  89. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  90. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  91. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  92. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  93. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
  94. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  95. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  96. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
  97. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  98. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  99. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  100. package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
  101. package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
  102. package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
  103. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  104. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  105. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  106. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
  107. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  108. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  109. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  110. package/src/transpiler/state/CodeGenState.ts +91 -22
  111. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  112. package/src/transpiler/types/ICachedFileEntry.ts +7 -0
  113. package/src/utils/LiteralUtils.ts +23 -0
  114. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  115. package/src/utils/cache/CacheManager.ts +13 -2
  116. package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
  117. package/src/utils/constants/TypeConstants.ts +13 -0
  118. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -3,10 +3,20 @@
3
3
  * Tests type classification, conversion validation, and literal validation
4
4
  */
5
5
  import { describe, it, expect, beforeEach } from "vitest";
6
+ import { CharStream, CommonTokenStream } from "antlr4ng";
6
7
  import TypeResolver from "../TypeResolver";
7
8
  import SymbolTable from "../../../logic/symbols/SymbolTable";
8
9
  import CodeGenState from "../../../state/CodeGenState";
9
10
  import TTypeInfo from "../types/TTypeInfo";
11
+ import { CNextLexer } from "../../../logic/parser/grammar/CNextLexer";
12
+ import { CNextParser } from "../../../logic/parser/grammar/CNextParser";
13
+
14
+ /** Parse a standalone C-Next expression into an ExpressionContext. */
15
+ function parseExpression(source: string) {
16
+ const lexer = new CNextLexer(CharStream.fromString(source));
17
+ const parser = new CNextParser(new CommonTokenStream(lexer));
18
+ return parser.expression();
19
+ }
10
20
 
11
21
  describe("TypeResolver", () => {
12
22
  let symbolTable: SymbolTable;
@@ -598,6 +608,89 @@ describe("TypeResolver", () => {
598
608
  });
599
609
  });
600
610
 
611
+ describe("getIntegerExpressionType", () => {
612
+ function setInt(name: string, baseType: string, bitWidth: number): void {
613
+ setTypeInfo(name, { baseType, bitWidth, isArray: false, isConst: false });
614
+ }
615
+
616
+ it("resolves a signed composite to its operand category and width", () => {
617
+ setInt("a", "i32", 32);
618
+ setInt("b", "i32", 32);
619
+ expect(
620
+ TypeResolver.getIntegerExpressionType(parseExpression("a + b")),
621
+ ).toBe("i32");
622
+ });
623
+
624
+ it("resolves an unsigned composite to its operand category and width", () => {
625
+ setInt("a", "u32", 32);
626
+ setInt("b", "u32", 32);
627
+ expect(
628
+ TypeResolver.getIntegerExpressionType(parseExpression("a | b")),
629
+ ).toBe("u32");
630
+ });
631
+
632
+ it("uses the widest operand width across a same-category composite", () => {
633
+ setInt("small", "u8", 8);
634
+ setInt("big", "u32", 32);
635
+ expect(
636
+ TypeResolver.getIntegerExpressionType(parseExpression("small + big")),
637
+ ).toBe("u32");
638
+ });
639
+
640
+ it("still resolves a simple variable via getExpressionType", () => {
641
+ setInt("x", "i16", 16);
642
+ expect(TypeResolver.getIntegerExpressionType(parseExpression("x"))).toBe(
643
+ "i16",
644
+ );
645
+ });
646
+
647
+ it("returns null when no integer variable leaf can be resolved", () => {
648
+ expect(
649
+ TypeResolver.getIntegerExpressionType(parseExpression("a + b")),
650
+ ).toBeNull();
651
+ });
652
+
653
+ it("ignores integer literals (contextually typed, not fixed-category)", () => {
654
+ setInt("a", "u32", 32);
655
+ expect(
656
+ TypeResolver.getIntegerExpressionType(parseExpression("a + 5")),
657
+ ).toBe("u32");
658
+ });
659
+
660
+ it("narrows a bit-extraction operand to the extracted width, not the variable's full width", () => {
661
+ setInt("a", "u32", 32);
662
+ setInt("b", "u64", 64);
663
+ // b[0, 32] is u32, so a + b[0, 32] is u32 — NOT u64. Typing it u64 would
664
+ // make slice codegen cast the composite to a wider type (MISRA 10.8).
665
+ expect(
666
+ TypeResolver.getIntegerExpressionType(parseExpression("a + b[0, 32]")),
667
+ ).toBe("u32");
668
+ });
669
+
670
+ it("types an array-element operand by its element type, ignoring the index variable's width", () => {
671
+ setTypeInfo("arr", {
672
+ baseType: "u8",
673
+ bitWidth: 8,
674
+ isArray: true,
675
+ isConst: false,
676
+ });
677
+ setInt("idx", "u64", 64);
678
+ setInt("a", "u8", 8);
679
+ // arr[idx] is u8 (the element); a wide index must not inflate the width.
680
+ expect(
681
+ TypeResolver.getIntegerExpressionType(parseExpression("a + arr[idx]")),
682
+ ).toBe("u8");
683
+ });
684
+
685
+ it("types a bit-extraction operand by the extracted width when it is the widest operand", () => {
686
+ setInt("b", "u64", 64);
687
+ setInt("c", "u8", 8);
688
+ expect(
689
+ TypeResolver.getIntegerExpressionType(parseExpression("b[0, 32] + c")),
690
+ ).toBe("u32");
691
+ });
692
+ });
693
+
601
694
  // ========================================================================
602
695
  // Postfix Expression Type Detection
603
696
  // ========================================================================
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Unit tests for TypeValidator.validateLoopConditionNotAlwaysTrue
3
+ * ADR-113 / Issue #1075: reject always-true literal loop conditions (E0707).
4
+ *
5
+ * Only the v0.2.18 literal slice — integer/bool literal comparisons decided
6
+ * without symbol resolution. Named constants, non-literal operands, and
7
+ * always-false conditions are out of scope (#1076).
8
+ */
9
+ import { describe, it, expect } from "vitest";
10
+ import { CharStream, CommonTokenStream } from "antlr4ng";
11
+ import { CNextLexer } from "../../../logic/parser/grammar/CNextLexer";
12
+ import { CNextParser } from "../../../logic/parser/grammar/CNextParser";
13
+ import TypeValidator from "../TypeValidator";
14
+
15
+ function parseCondition(text: string) {
16
+ const charStream = CharStream.fromString(text);
17
+ const lexer = new CNextLexer(charStream);
18
+ const tokenStream = new CommonTokenStream(lexer);
19
+ const parser = new CNextParser(tokenStream);
20
+ return parser.expression();
21
+ }
22
+
23
+ function isFlaggedAlwaysTrue(text: string): boolean {
24
+ try {
25
+ TypeValidator.validateLoopConditionNotAlwaysTrue(parseCondition(text));
26
+ return false;
27
+ } catch (error) {
28
+ return /E0707/.test((error as Error).message);
29
+ }
30
+ }
31
+
32
+ describe("TypeValidator.validateLoopConditionNotAlwaysTrue (E0707)", () => {
33
+ describe("flags always-true literal comparisons", () => {
34
+ it.each([
35
+ "1 = 1",
36
+ "true = true",
37
+ "false = false",
38
+ "1 != 2",
39
+ "5 > 3",
40
+ "3 < 5",
41
+ "3 >= 3",
42
+ "5 <= 5",
43
+ "0x10 = 16",
44
+ "0XFF = 255", // uppercase hex prefix
45
+ "0b10 = 2",
46
+ "5u8 = 5", // type-suffixed literal
47
+ ])("flags %s", (condition) => {
48
+ expect(isFlaggedAlwaysTrue(condition)).toBe(true);
49
+ });
50
+
51
+ it("includes the condition text and steers to forever", () => {
52
+ let message = "";
53
+ try {
54
+ TypeValidator.validateLoopConditionNotAlwaysTrue(
55
+ parseCondition("1 = 1"),
56
+ );
57
+ } catch (error) {
58
+ message = (error as Error).message;
59
+ }
60
+ expect(message).toContain(
61
+ "Error E0707: loop condition '1=1' is always true",
62
+ );
63
+ expect(message).toContain("forever { ... }");
64
+ });
65
+ });
66
+
67
+ describe("does NOT flag always-false comparisons (out of scope, #1076)", () => {
68
+ it.each(["1 = 2", "true = false", "1 != 1", "5 < 3", "3 > 5", "5 <= 3"])(
69
+ "allows %s",
70
+ (condition) => {
71
+ expect(isFlaggedAlwaysTrue(condition)).toBe(false);
72
+ },
73
+ );
74
+ });
75
+
76
+ describe("does NOT flag non-literal or compound conditions (needs folding, #1076)", () => {
77
+ it.each([
78
+ "x = 1", // identifier operand
79
+ "MAX > 0", // named constant
80
+ "a = b", // two identifiers
81
+ "5.0 > 3.0", // float operands
82
+ "1 + 0 = 1", // compound left operand
83
+ "1 = 1 || 0 > 5", // logical-or combination
84
+ "1 = 1 && 2 = 2", // logical-and combination
85
+ "0777 = 777", // leading-zero literal: C reads it as octal (511), so a
86
+ // decimal parse (777) would diverge — skipped, not wrongly flagged
87
+ ])("allows %s", (condition) => {
88
+ expect(isFlaggedAlwaysTrue(condition)).toBe(false);
89
+ });
90
+ });
91
+ });
@@ -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
  },
@@ -1729,15 +1740,37 @@ describe("TypeValidator", () => {
1729
1740
  // ========================================================================
1730
1741
 
1731
1742
  describe("validateTernaryCondition", () => {
1732
- it("allows conditions with || operator", () => {
1743
+ it("throws for || of bare value operands (each operand must be a comparison)", () => {
1733
1744
  setupState();
1734
1745
  const ctx = createMockOrExpression("a || b", { hasOr: true });
1746
+ expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
1747
+ "E0701",
1748
+ );
1749
+ });
1750
+
1751
+ it("allows || of comparison operands", () => {
1752
+ setupState();
1753
+ const ctx = createMockOrExpression("a = 1 || b = 2", {
1754
+ hasOr: true,
1755
+ hasEquality: true,
1756
+ });
1735
1757
  expect(() => TypeValidator.validateTernaryCondition(ctx)).not.toThrow();
1736
1758
  });
1737
1759
 
1738
- it("allows conditions with && operator", () => {
1760
+ it("throws for && of bare value operands (each operand must be a comparison)", () => {
1739
1761
  setupState();
1740
1762
  const ctx = createMockOrExpression("a && b", { hasAnd: true });
1763
+ expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
1764
+ "E0701",
1765
+ );
1766
+ });
1767
+
1768
+ it("allows && of comparison operands", () => {
1769
+ setupState();
1770
+ const ctx = createMockOrExpression("a = 1 && b = 2", {
1771
+ hasAnd: true,
1772
+ hasEquality: true,
1773
+ });
1741
1774
  expect(() => TypeValidator.validateTernaryCondition(ctx)).not.toThrow();
1742
1775
  });
1743
1776
 
@@ -1757,7 +1790,7 @@ describe("TypeValidator", () => {
1757
1790
  setupState();
1758
1791
  const ctx = createMockOrExpression("flag");
1759
1792
  expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
1760
- "Ternary condition must be a boolean expression",
1793
+ "E0701",
1761
1794
  );
1762
1795
  });
1763
1796
 
@@ -1768,7 +1801,7 @@ describe("TypeValidator", () => {
1768
1801
  andExpression: antlrArray([]),
1769
1802
  } as unknown as Parser.OrExpressionContext;
1770
1803
  expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
1771
- "Ternary condition must be a boolean expression",
1804
+ "E0701",
1772
1805
  );
1773
1806
  });
1774
1807
 
@@ -1780,7 +1813,7 @@ describe("TypeValidator", () => {
1780
1813
  andExpression: antlrArray([andExpr]),
1781
1814
  } as unknown as Parser.OrExpressionContext;
1782
1815
  expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
1783
- "Ternary condition must be a boolean expression",
1816
+ "E0701",
1784
1817
  );
1785
1818
  });
1786
1819
 
@@ -1793,7 +1826,7 @@ describe("TypeValidator", () => {
1793
1826
  andExpression: antlrArray([andExpr]),
1794
1827
  } as unknown as Parser.OrExpressionContext;
1795
1828
  expect(() => TypeValidator.validateTernaryCondition(ctx)).toThrow(
1796
- "Ternary condition must be a boolean expression",
1829
+ "E0701",
1797
1830
  );
1798
1831
  });
1799
1832
  });
@@ -1887,17 +1920,39 @@ describe("TypeValidator", () => {
1887
1920
  ).not.toThrow();
1888
1921
  });
1889
1922
 
1890
- it("allows conditions with && operator", () => {
1923
+ it("throws for && of bare value operands (each operand must be a comparison)", () => {
1891
1924
  setupState();
1892
1925
  const ctx = createFullDoWhileExpression("a && b", { hasAnd: true });
1926
+ expect(() =>
1927
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1928
+ ).toThrow("E0701");
1929
+ });
1930
+
1931
+ it("allows && of comparison operands", () => {
1932
+ setupState();
1933
+ const ctx = createFullDoWhileExpression("a = 1 && b = 2", {
1934
+ hasAnd: true,
1935
+ hasEquality: true,
1936
+ });
1893
1937
  expect(() =>
1894
1938
  TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1895
1939
  ).not.toThrow();
1896
1940
  });
1897
1941
 
1898
- it("allows conditions with || operator", () => {
1942
+ it("throws for || of bare value operands (each operand must be a comparison)", () => {
1899
1943
  setupState();
1900
1944
  const ctx = createFullDoWhileExpression("a || b", { hasOr: true });
1945
+ expect(() =>
1946
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1947
+ ).toThrow("E0701");
1948
+ });
1949
+
1950
+ it("allows || of comparison operands", () => {
1951
+ setupState();
1952
+ const ctx = createFullDoWhileExpression("a = 1 || b = 2", {
1953
+ hasOr: true,
1954
+ hasEquality: true,
1955
+ });
1901
1956
  expect(() =>
1902
1957
  TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1903
1958
  ).not.toThrow();
@@ -1927,7 +1982,7 @@ describe("TypeValidator", () => {
1927
1982
  ).toThrow("E0701");
1928
1983
  });
1929
1984
 
1930
- it("allows boolean literals", () => {
1985
+ it("throws for boolean literal condition (Issue #1042)", () => {
1931
1986
  setupState();
1932
1987
  // Create full mock expression tree where getText returns "true"
1933
1988
  const bitwiseOrExpr = {
@@ -1958,10 +2013,10 @@ describe("TypeValidator", () => {
1958
2013
  } as unknown as Parser.ExpressionContext;
1959
2014
  expect(() =>
1960
2015
  TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1961
- ).not.toThrow();
2016
+ ).toThrow("E0701");
1962
2017
  });
1963
2018
 
1964
- it("allows negation expressions", () => {
2019
+ it("throws for negation expression (Issue #1042)", () => {
1965
2020
  setupState();
1966
2021
  const bitwiseOrExpr = {
1967
2022
  bitwiseXorExpression: antlrArray([]),
@@ -1991,10 +2046,10 @@ describe("TypeValidator", () => {
1991
2046
  } as unknown as Parser.ExpressionContext;
1992
2047
  expect(() =>
1993
2048
  TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1994
- ).not.toThrow();
2049
+ ).toThrow("E0701");
1995
2050
  });
1996
2051
 
1997
- it("allows bool type variables", () => {
2052
+ it("throws for bare bool type variable (Issue #1042)", () => {
1998
2053
  const typeRegistry = new Map<string, TTypeInfo>([
1999
2054
  [
2000
2055
  "isReady",
@@ -2030,7 +2085,7 @@ describe("TypeValidator", () => {
2030
2085
  } as unknown as Parser.ExpressionContext;
2031
2086
  expect(() =>
2032
2087
  TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2033
- ).not.toThrow();
2088
+ ).toThrow("E0701");
2034
2089
  });
2035
2090
 
2036
2091
  it("shows help message in error", () => {
@@ -2041,6 +2096,34 @@ describe("TypeValidator", () => {
2041
2096
  ).toThrow("help: use explicit comparison: count > 0 or count != 0");
2042
2097
  });
2043
2098
 
2099
+ it("suggests `= true` for a bare bool operand (Issue #1042)", () => {
2100
+ const typeRegistry = new Map<string, TTypeInfo>([
2101
+ [
2102
+ "ready",
2103
+ { baseType: "bool", bitWidth: 8, isArray: false, isConst: false },
2104
+ ],
2105
+ ]);
2106
+ setupState({ typeRegistry });
2107
+ const ctx = createFullDoWhileExpression("ready");
2108
+ expect(() =>
2109
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2110
+ ).toThrow("help: use explicit comparison: ready = true");
2111
+ });
2112
+
2113
+ it("suggests `= false` for a negated bool operand (Issue #1042)", () => {
2114
+ const typeRegistry = new Map<string, TTypeInfo>([
2115
+ [
2116
+ "ready",
2117
+ { baseType: "bool", bitWidth: 8, isArray: false, isConst: false },
2118
+ ],
2119
+ ]);
2120
+ setupState({ typeRegistry });
2121
+ const ctx = createFullDoWhileExpression("!ready");
2122
+ expect(() =>
2123
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2124
+ ).toThrow("help: use explicit comparison: ready = false");
2125
+ });
2126
+
2044
2127
  it("throws when no andExpression", () => {
2045
2128
  setupState();
2046
2129
  const orExpr = {
@@ -415,9 +415,22 @@ class AssignmentClassifier {
415
415
  const firstId = ctx.identifiers[0];
416
416
 
417
417
  if (ctx.hasArrayAccess) {
418
+ // Direct register: global.REG.MEMBER[bit]
418
419
  if (CodeGenState.symbols!.knownRegisters.has(firstId)) {
419
420
  return AssignmentKind.GLOBAL_REGISTER_BIT;
420
421
  }
422
+ // Scoped register: global.Scope.REG.MEMBER[bit] — mirror
423
+ // classifyRegisterBitAccess so the bit-range mask/shift is expanded
424
+ // rather than emitting a literal subscript (Issue #1052).
425
+ if (
426
+ CodeGenState.isKnownScope(firstId) &&
427
+ ctx.identifiers.length >= 3 &&
428
+ CodeGenState.symbols!.knownRegisters.has(
429
+ `${firstId}_${ctx.identifiers[1]}`,
430
+ )
431
+ ) {
432
+ return AssignmentKind.GLOBAL_REGISTER_BIT;
433
+ }
421
434
  return AssignmentKind.GLOBAL_ARRAY;
422
435
  }
423
436
 
@@ -85,7 +85,7 @@ enum AssignmentKind {
85
85
  /** matrix[i][j] <- value (multi-dimensional array element) */
86
86
  MULTI_DIM_ARRAY_ELEMENT,
87
87
 
88
- /** buffer[0, 10] <- source (slice assignment with memcpy) */
88
+ /** buffer[0, 10] <- source (slice assignment: per-element little-endian writes, ADR-007/#1081) */
89
89
  ARRAY_SLICE,
90
90
 
91
91
  // === Special operations ===
@@ -15,12 +15,6 @@ import BitUtils from "../../../../../utils/BitUtils";
15
15
  import TAssignmentHandler from "./TAssignmentHandler";
16
16
  import RegisterUtils from "./RegisterUtils";
17
17
  import CodeGenState from "../../../../state/CodeGenState";
18
- import type ICodeGenApi from "../../types/ICodeGenApi";
19
-
20
- /** Get typed generator reference */
21
- function gen(): ICodeGenApi {
22
- return CodeGenState.generator as ICodeGenApi;
23
- }
24
18
 
25
19
  /**
26
20
  * Common handler for global access patterns (GLOBAL_MEMBER and GLOBAL_ARRAY).
@@ -32,10 +26,15 @@ function handleGlobalAccess(ctx: IAssignmentContext): string {
32
26
 
33
27
  // Validate cross-scope visibility if first id is a scope
34
28
  if (CodeGenState.isKnownScope(firstId) && ctx.identifiers.length >= 2) {
35
- gen().validateCrossScopeVisibility(firstId, ctx.identifiers[1]);
29
+ CodeGenState.requireGenerator().validateCrossScopeVisibility(
30
+ firstId,
31
+ ctx.identifiers[1],
32
+ );
36
33
  }
37
34
 
38
- const target = gen().generateAssignmentTarget(ctx.targetCtx);
35
+ const target = CodeGenState.requireGenerator().generateAssignmentTarget(
36
+ ctx.targetCtx,
37
+ );
39
38
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
40
39
  }
41
40
 
@@ -49,7 +48,9 @@ function handleThisAccess(ctx: IAssignmentContext): string {
49
48
  throw new Error("Error: 'this' can only be used inside a scope");
50
49
  }
51
50
 
52
- const target = gen().generateAssignmentTarget(ctx.targetCtx);
51
+ const target = CodeGenState.requireGenerator().generateAssignmentTarget(
52
+ ctx.targetCtx,
53
+ );
53
54
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
54
55
  }
55
56
 
@@ -100,7 +101,9 @@ function handleGlobalRegisterBit(ctx: IAssignmentContext): string {
100
101
  }
101
102
 
102
103
  // Single bit
103
- const bitIndex = gen().generateExpression(ctx.subscripts[0]);
104
+ const bitIndex = CodeGenState.requireGenerator().generateExpression(
105
+ ctx.subscripts[0],
106
+ );
104
107
 
105
108
  if (isWriteOnly) {
106
109
  if (ctx.generatedValue === "false" || ctx.generatedValue === "0") {
@@ -126,7 +129,10 @@ function handleGlobalRegisterBit(ctx: IAssignmentContext): string {
126
129
  */
127
130
  function handleMemberChain(ctx: IAssignmentContext): string {
128
131
  // Check if this is bit access on a struct member
129
- const bitAnalysis = gen().analyzeMemberChainForBitAccess(ctx.targetCtx);
132
+ const bitAnalysis =
133
+ CodeGenState.requireGenerator().analyzeMemberChainForBitAccess(
134
+ ctx.targetCtx,
135
+ );
130
136
 
131
137
  if (bitAnalysis.isBitAccess) {
132
138
  // Validate compound operators not supported for bit access
@@ -144,7 +150,9 @@ function handleMemberChain(ctx: IAssignmentContext): string {
144
150
  }
145
151
 
146
152
  // Normal member chain assignment
147
- const target = gen().generateAssignmentTarget(ctx.targetCtx);
153
+ const target = CodeGenState.requireGenerator().generateAssignmentTarget(
154
+ ctx.targetCtx,
155
+ );
148
156
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
149
157
  }
150
158