c-next 0.2.15 → 0.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/index.js +1403 -427
- package/dist/index.js.map +3 -3
- package/grammar/CNext.g4 +4 -0
- package/package.json +5 -1
- package/src/transpiler/Transpiler.ts +90 -22
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
- package/src/transpiler/logic/symbols/SymbolTable.ts +54 -12
- package/src/transpiler/logic/symbols/SymbolUtils.ts +21 -8
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +39 -4
- package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +2 -1
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +2 -1
- package/src/transpiler/logic/symbols/cnext/__tests__/CNextResolver.integration.test.ts +5 -2
- package/src/transpiler/logic/symbols/cnext/__tests__/ScopeCollector.test.ts +5 -2
- package/src/transpiler/logic/symbols/cnext/collectors/ScopeCollector.ts +7 -2
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/logic/symbols/cpp/utils/DeclaratorUtils.ts +4 -2
- package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +167 -18
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +32 -8
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +91 -1
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +71 -6
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/symbols/c/ICFieldInfo.ts +6 -2
- package/src/transpiler/types/symbols/cpp/ICppFieldInfo.ts +5 -2
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/ScopeUtils.ts +19 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/__tests__/ScopeUtils.test.ts +10 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -1504,7 +1504,7 @@ describe("CodeGenerator", () => {
|
|
|
1504
1504
|
|
|
1505
1505
|
it("should generate multi-dimensional array", () => {
|
|
1506
1506
|
const source = `
|
|
1507
|
-
u32[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
|
|
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]
|
|
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]
|
|
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 {
|
|
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
|
-
//
|
|
6954
|
-
|
|
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 {
|
|
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
|
-
//
|
|
7044
|
-
|
|
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 {
|
|
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
|
-
//
|
|
7087
|
-
|
|
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]
|
|
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
|
-
|
|
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
|
|
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
|
|
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>
|
|
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]
|
|
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
|
|
14896
|
-
it("should
|
|
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
|
-
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
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
|
|
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
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
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>
|
|
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>
|
|
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
|
-
|
|
43
|
-
|
|
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 #
|
|
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 = `${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|