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.
- package/README.md +18 -2
- package/dist/index.js +8897 -6260
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +12 -0
- package/package.json +4 -2
- package/src/transpiler/Transpiler.ts +376 -48
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
- package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
- package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
- package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
- package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
- package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
- package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
- package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
- package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
- package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
- package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
- package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
- package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
- package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
- package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
- package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
- package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
- package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
- package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
- package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
- package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +91 -22
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/ICachedFileEntry.ts +7 -0
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/cache/CacheManager.ts +13 -2
- package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
- package/src/utils/constants/TypeConstants.ts +13 -0
- 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("
|
|
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("
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
1829
|
+
"E0701",
|
|
1797
1830
|
);
|
|
1798
1831
|
});
|
|
1799
1832
|
});
|
|
@@ -1887,17 +1920,39 @@ describe("TypeValidator", () => {
|
|
|
1887
1920
|
).not.toThrow();
|
|
1888
1921
|
});
|
|
1889
1922
|
|
|
1890
|
-
it("
|
|
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("
|
|
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("
|
|
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
|
-
).
|
|
2016
|
+
).toThrow("E0701");
|
|
1962
2017
|
});
|
|
1963
2018
|
|
|
1964
|
-
it("
|
|
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
|
-
).
|
|
2049
|
+
).toThrow("E0701");
|
|
1995
2050
|
});
|
|
1996
2051
|
|
|
1997
|
-
it("
|
|
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
|
-
).
|
|
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
|
|
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
|
-
|
|
29
|
+
CodeGenState.requireGenerator().validateCrossScopeVisibility(
|
|
30
|
+
firstId,
|
|
31
|
+
ctx.identifiers[1],
|
|
32
|
+
);
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
const target =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
153
|
+
const target = CodeGenState.requireGenerator().generateAssignmentTarget(
|
|
154
|
+
ctx.targetCtx,
|
|
155
|
+
);
|
|
148
156
|
return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
|
|
149
157
|
}
|
|
150
158
|
|