c-next 0.1.61 → 0.1.63
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 +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- package/src/transpiler/types/ITranspileContribution.ts +0 -32
|
@@ -1466,7 +1466,7 @@ describe("CodeGenerator", () => {
|
|
|
1466
1466
|
describe("Array declarations", () => {
|
|
1467
1467
|
it("should generate array declaration", () => {
|
|
1468
1468
|
const source = `
|
|
1469
|
-
u32
|
|
1469
|
+
u32[10] arr;
|
|
1470
1470
|
void main() { }
|
|
1471
1471
|
`;
|
|
1472
1472
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -1485,7 +1485,7 @@ describe("CodeGenerator", () => {
|
|
|
1485
1485
|
|
|
1486
1486
|
it("should generate array with initializer", () => {
|
|
1487
1487
|
const source = `
|
|
1488
|
-
u32
|
|
1488
|
+
u32[3] arr <- [1, 2, 3];
|
|
1489
1489
|
void main() { }
|
|
1490
1490
|
`;
|
|
1491
1491
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -1505,7 +1505,7 @@ describe("CodeGenerator", () => {
|
|
|
1505
1505
|
|
|
1506
1506
|
it("should generate multi-dimensional array", () => {
|
|
1507
1507
|
const source = `
|
|
1508
|
-
u32
|
|
1508
|
+
u32[3] matrix[3];
|
|
1509
1509
|
void main() { }
|
|
1510
1510
|
`;
|
|
1511
1511
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -2586,7 +2586,7 @@ describe("CodeGenerator", () => {
|
|
|
2586
2586
|
describe("Array element assignment", () => {
|
|
2587
2587
|
it("should generate array element assignment", () => {
|
|
2588
2588
|
const source = `
|
|
2589
|
-
wrap u32
|
|
2589
|
+
wrap u32[10] arr;
|
|
2590
2590
|
void main() {
|
|
2591
2591
|
arr[0] <- 42;
|
|
2592
2592
|
arr[5] <- 100;
|
|
@@ -2734,7 +2734,7 @@ describe("CodeGenerator", () => {
|
|
|
2734
2734
|
it("should generate struct with array field", () => {
|
|
2735
2735
|
const source = `
|
|
2736
2736
|
struct Buffer {
|
|
2737
|
-
u8
|
|
2737
|
+
u8[64] data;
|
|
2738
2738
|
u32 length;
|
|
2739
2739
|
}
|
|
2740
2740
|
`;
|
|
@@ -3297,7 +3297,7 @@ describe("CodeGenerator", () => {
|
|
|
3297
3297
|
it("should generate struct with array member access", () => {
|
|
3298
3298
|
const source = `
|
|
3299
3299
|
struct Buffer {
|
|
3300
|
-
u8
|
|
3300
|
+
u8[64] data;
|
|
3301
3301
|
}
|
|
3302
3302
|
Buffer buf;
|
|
3303
3303
|
void main() {
|
|
@@ -3427,7 +3427,7 @@ describe("CodeGenerator", () => {
|
|
|
3427
3427
|
describe("Const array", () => {
|
|
3428
3428
|
it("should generate const array", () => {
|
|
3429
3429
|
const source = `
|
|
3430
|
-
const u32
|
|
3430
|
+
const u32[4] LOOKUP <- [10, 20, 30, 40];
|
|
3431
3431
|
void main() { }
|
|
3432
3432
|
`;
|
|
3433
3433
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -3853,7 +3853,7 @@ describe("CodeGenerator", () => {
|
|
|
3853
3853
|
describe("Array fill-all syntax", () => {
|
|
3854
3854
|
it("should generate fill-all array [0*]", () => {
|
|
3855
3855
|
const source = `
|
|
3856
|
-
u32
|
|
3856
|
+
u32[10] data <- [0*];
|
|
3857
3857
|
`;
|
|
3858
3858
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
3859
3859
|
const generator = new CodeGenerator();
|
|
@@ -3872,7 +3872,7 @@ describe("CodeGenerator", () => {
|
|
|
3872
3872
|
|
|
3873
3873
|
it("should generate fill-all array with non-zero value", () => {
|
|
3874
3874
|
const source = `
|
|
3875
|
-
u8
|
|
3875
|
+
u8[4] buffer <- [255*];
|
|
3876
3876
|
`;
|
|
3877
3877
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
3878
3878
|
const generator = new CodeGenerator();
|
|
@@ -3893,7 +3893,7 @@ describe("CodeGenerator", () => {
|
|
|
3893
3893
|
describe("Nested array initializers", () => {
|
|
3894
3894
|
it("should generate 2D array initializer", () => {
|
|
3895
3895
|
const source = `
|
|
3896
|
-
u32
|
|
3896
|
+
u32[2] matrix[3] <- [[1, 2, 3], [4, 5, 6]];
|
|
3897
3897
|
`;
|
|
3898
3898
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
3899
3899
|
const generator = new CodeGenerator();
|
|
@@ -3914,7 +3914,7 @@ describe("CodeGenerator", () => {
|
|
|
3914
3914
|
it("should generate array of struct initializers", () => {
|
|
3915
3915
|
const source = `
|
|
3916
3916
|
struct Point { i32 x; i32 y; }
|
|
3917
|
-
Point
|
|
3917
|
+
Point[2] points <- [{x: 1, y: 2}, {x: 3, y: 4}];
|
|
3918
3918
|
`;
|
|
3919
3919
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
3920
3920
|
const generator = new CodeGenerator();
|
|
@@ -4184,7 +4184,7 @@ describe("CodeGenerator", () => {
|
|
|
4184
4184
|
it("should resolve const as array dimension at file scope", () => {
|
|
4185
4185
|
const source = `
|
|
4186
4186
|
const u32 SIZE <- 10;
|
|
4187
|
-
u32
|
|
4187
|
+
u32[SIZE] data;
|
|
4188
4188
|
`;
|
|
4189
4189
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
4190
4190
|
const generator = new CodeGenerator();
|
|
@@ -4205,7 +4205,7 @@ describe("CodeGenerator", () => {
|
|
|
4205
4205
|
describe("Multi-dimensional array", () => {
|
|
4206
4206
|
it("should generate 3D array declaration", () => {
|
|
4207
4207
|
const source = `
|
|
4208
|
-
u8
|
|
4208
|
+
u8[2] cube[3][4];
|
|
4209
4209
|
`;
|
|
4210
4210
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
4211
4211
|
const generator = new CodeGenerator();
|
|
@@ -4686,7 +4686,7 @@ describe("CodeGenerator", () => {
|
|
|
4686
4686
|
describe("Array indexing with expressions", () => {
|
|
4687
4687
|
it("should generate array index with arithmetic", () => {
|
|
4688
4688
|
const source = `
|
|
4689
|
-
u32
|
|
4689
|
+
u32[10] data;
|
|
4690
4690
|
wrap u32 i <- 2;
|
|
4691
4691
|
void main() {
|
|
4692
4692
|
data[i * 2 + 1] <- 42;
|
|
@@ -4877,7 +4877,7 @@ describe("CodeGenerator", () => {
|
|
|
4877
4877
|
describe("Array length", () => {
|
|
4878
4878
|
it("should generate array length using sizeof", () => {
|
|
4879
4879
|
const source = `
|
|
4880
|
-
u32
|
|
4880
|
+
u32[10] data;
|
|
4881
4881
|
u32 len <- data.length;
|
|
4882
4882
|
`;
|
|
4883
4883
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -5285,7 +5285,7 @@ describe("CodeGenerator", () => {
|
|
|
5285
5285
|
it("should pass array without address-of operator", () => {
|
|
5286
5286
|
const source = `
|
|
5287
5287
|
void processData(u32 data[]) { }
|
|
5288
|
-
u32
|
|
5288
|
+
u32[5] myData;
|
|
5289
5289
|
void main() {
|
|
5290
5290
|
processData(myData);
|
|
5291
5291
|
}
|
|
@@ -5310,7 +5310,7 @@ describe("CodeGenerator", () => {
|
|
|
5310
5310
|
it("should generate struct with array field", () => {
|
|
5311
5311
|
const source = `
|
|
5312
5312
|
struct Buffer {
|
|
5313
|
-
u8
|
|
5313
|
+
u8[64] data;
|
|
5314
5314
|
u32 length;
|
|
5315
5315
|
}
|
|
5316
5316
|
`;
|
|
@@ -5694,7 +5694,7 @@ describe("CodeGenerator", () => {
|
|
|
5694
5694
|
describe("Array bounds checking", () => {
|
|
5695
5695
|
it("should throw error for out-of-bounds constant index", () => {
|
|
5696
5696
|
const source = `
|
|
5697
|
-
u32
|
|
5697
|
+
u32[5] data;
|
|
5698
5698
|
void main() {
|
|
5699
5699
|
data[10] <- 1;
|
|
5700
5700
|
}
|
|
@@ -5899,7 +5899,7 @@ describe("CodeGenerator", () => {
|
|
|
5899
5899
|
describe("Sizeof variable", () => {
|
|
5900
5900
|
it("should generate sizeof for variable", () => {
|
|
5901
5901
|
const source = `
|
|
5902
|
-
u32
|
|
5902
|
+
u32[10] arr;
|
|
5903
5903
|
u32 size <- sizeof(arr);
|
|
5904
5904
|
`;
|
|
5905
5905
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -5921,7 +5921,7 @@ describe("CodeGenerator", () => {
|
|
|
5921
5921
|
it("should resolve const to literal for file-scope array", () => {
|
|
5922
5922
|
const source = `
|
|
5923
5923
|
const u32 BUFFER_SIZE <- 256;
|
|
5924
|
-
u8
|
|
5924
|
+
u8[BUFFER_SIZE] buffer;
|
|
5925
5925
|
`;
|
|
5926
5926
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
5927
5927
|
const generator = new CodeGenerator();
|
|
@@ -6056,7 +6056,7 @@ describe("CodeGenerator", () => {
|
|
|
6056
6056
|
it("should generate struct with array field access", () => {
|
|
6057
6057
|
const source = `
|
|
6058
6058
|
struct Message {
|
|
6059
|
-
u8
|
|
6059
|
+
u8[8] data;
|
|
6060
6060
|
u8 length;
|
|
6061
6061
|
}
|
|
6062
6062
|
Message msg;
|
|
@@ -6166,7 +6166,7 @@ describe("CodeGenerator", () => {
|
|
|
6166
6166
|
const source = `
|
|
6167
6167
|
void setup() {
|
|
6168
6168
|
const u32 SIZE <- 5;
|
|
6169
|
-
u32
|
|
6169
|
+
u32[SIZE] data;
|
|
6170
6170
|
}
|
|
6171
6171
|
`;
|
|
6172
6172
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -6181,7 +6181,8 @@ describe("CodeGenerator", () => {
|
|
|
6181
6181
|
});
|
|
6182
6182
|
|
|
6183
6183
|
expect(code).toContain("SIZE");
|
|
6184
|
-
|
|
6184
|
+
// Const value is evaluated at compile time
|
|
6185
|
+
expect(code).toContain("data[5]");
|
|
6185
6186
|
});
|
|
6186
6187
|
});
|
|
6187
6188
|
|
|
@@ -6666,7 +6667,7 @@ describe("CodeGenerator", () => {
|
|
|
6666
6667
|
|
|
6667
6668
|
describe("Array with literal size", () => {
|
|
6668
6669
|
it("should generate array with integer literal size", () => {
|
|
6669
|
-
const source = `u32
|
|
6670
|
+
const source = `u32[100] buffer;`;
|
|
6670
6671
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
6671
6672
|
const generator = new CodeGenerator();
|
|
6672
6673
|
const symbolTable = new SymbolTable();
|
|
@@ -6793,7 +6794,7 @@ describe("CodeGenerator", () => {
|
|
|
6793
6794
|
|
|
6794
6795
|
describe("Array initialization with values", () => {
|
|
6795
6796
|
it("should generate array with initialization list", () => {
|
|
6796
|
-
const source = `u8
|
|
6797
|
+
const source = `u8[4] data <- [1, 2, 3, 4];`;
|
|
6797
6798
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
6798
6799
|
const generator = new CodeGenerator();
|
|
6799
6800
|
const symbolTable = new SymbolTable();
|
|
@@ -6814,7 +6815,7 @@ describe("CodeGenerator", () => {
|
|
|
6814
6815
|
const source = `
|
|
6815
6816
|
void process(u8 arr[]) { }
|
|
6816
6817
|
void main() {
|
|
6817
|
-
u8
|
|
6818
|
+
u8[10] local;
|
|
6818
6819
|
process(local);
|
|
6819
6820
|
}
|
|
6820
6821
|
`;
|
|
@@ -7081,7 +7082,7 @@ describe("CodeGenerator", () => {
|
|
|
7081
7082
|
it("should initialize POD arrays to {0}", () => {
|
|
7082
7083
|
const source = `
|
|
7083
7084
|
void main() {
|
|
7084
|
-
u32
|
|
7085
|
+
u32[10] values;
|
|
7085
7086
|
}
|
|
7086
7087
|
`;
|
|
7087
7088
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -7102,7 +7103,7 @@ describe("CodeGenerator", () => {
|
|
|
7102
7103
|
const source = `
|
|
7103
7104
|
struct Point { i32 x; i32 y; }
|
|
7104
7105
|
void main() {
|
|
7105
|
-
Point
|
|
7106
|
+
Point[5] points;
|
|
7106
7107
|
}
|
|
7107
7108
|
`;
|
|
7108
7109
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -7347,7 +7348,7 @@ describe("CodeGenerator", () => {
|
|
|
7347
7348
|
it("should generate struct array member write", () => {
|
|
7348
7349
|
const source = `
|
|
7349
7350
|
struct Item { u32 value; }
|
|
7350
|
-
Item
|
|
7351
|
+
Item[3] items;
|
|
7351
7352
|
void main() {
|
|
7352
7353
|
items[0].value <- 42;
|
|
7353
7354
|
}
|
|
@@ -7932,7 +7933,7 @@ describe("CodeGenerator", () => {
|
|
|
7932
7933
|
const source = `
|
|
7933
7934
|
void callee(u32 arr[10]) { }
|
|
7934
7935
|
void test() {
|
|
7935
|
-
u32
|
|
7936
|
+
u32[10] data;
|
|
7936
7937
|
callee(data);
|
|
7937
7938
|
}
|
|
7938
7939
|
`;
|
|
@@ -7976,7 +7977,7 @@ describe("CodeGenerator", () => {
|
|
|
7976
7977
|
const source = `
|
|
7977
7978
|
void callee(u32 val) { }
|
|
7978
7979
|
void test() {
|
|
7979
|
-
u32
|
|
7980
|
+
u32[10] arr;
|
|
7980
7981
|
callee(arr[0]);
|
|
7981
7982
|
}
|
|
7982
7983
|
`;
|
|
@@ -8426,7 +8427,7 @@ describe("CodeGenerator", () => {
|
|
|
8426
8427
|
it("should handle sizeof on variable", () => {
|
|
8427
8428
|
const source = `
|
|
8428
8429
|
void test() {
|
|
8429
|
-
u32
|
|
8430
|
+
u32[10] arr;
|
|
8430
8431
|
u32 size <- sizeof(arr);
|
|
8431
8432
|
}
|
|
8432
8433
|
`;
|
|
@@ -8491,7 +8492,7 @@ describe("CodeGenerator", () => {
|
|
|
8491
8492
|
it("should handle single index array access", () => {
|
|
8492
8493
|
const source = `
|
|
8493
8494
|
void test() {
|
|
8494
|
-
u32
|
|
8495
|
+
u32[10] arr;
|
|
8495
8496
|
u32 val <- arr[5];
|
|
8496
8497
|
}
|
|
8497
8498
|
`;
|
|
@@ -8575,7 +8576,7 @@ describe("CodeGenerator", () => {
|
|
|
8575
8576
|
it("should handle C++ mode array access", () => {
|
|
8576
8577
|
const source = `
|
|
8577
8578
|
void test() {
|
|
8578
|
-
u32
|
|
8579
|
+
u32[10] arr;
|
|
8579
8580
|
u32 val <- arr[5];
|
|
8580
8581
|
}
|
|
8581
8582
|
`;
|
|
@@ -8597,7 +8598,7 @@ describe("CodeGenerator", () => {
|
|
|
8597
8598
|
it("should handle multi-dimensional array access", () => {
|
|
8598
8599
|
const source = `
|
|
8599
8600
|
void test() {
|
|
8600
|
-
u32
|
|
8601
|
+
u32[3] matrix[3];
|
|
8601
8602
|
u32 val <- matrix[1][2];
|
|
8602
8603
|
}
|
|
8603
8604
|
`;
|
|
@@ -8778,7 +8779,7 @@ describe("CodeGenerator", () => {
|
|
|
8778
8779
|
const source = `
|
|
8779
8780
|
struct Point { u32 x; u32 y; }
|
|
8780
8781
|
void test() {
|
|
8781
|
-
Point
|
|
8782
|
+
Point[10] points;
|
|
8782
8783
|
u32 x <- points[0].x;
|
|
8783
8784
|
}
|
|
8784
8785
|
`;
|
|
@@ -9141,7 +9142,7 @@ describe("CodeGenerator", () => {
|
|
|
9141
9142
|
it("should resolve const values in expressions", () => {
|
|
9142
9143
|
const source = `
|
|
9143
9144
|
const u32 MAX_SIZE <- 100;
|
|
9144
|
-
u32
|
|
9145
|
+
u32[MAX_SIZE] buffer;
|
|
9145
9146
|
`;
|
|
9146
9147
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
9147
9148
|
const generator = new CodeGenerator();
|
|
@@ -9210,7 +9211,7 @@ describe("CodeGenerator", () => {
|
|
|
9210
9211
|
it("should handle array access in assignment target", () => {
|
|
9211
9212
|
const source = `
|
|
9212
9213
|
void test() {
|
|
9213
|
-
u32
|
|
9214
|
+
u32[10] arr;
|
|
9214
9215
|
arr[5] <- 42;
|
|
9215
9216
|
}
|
|
9216
9217
|
`;
|
|
@@ -9716,7 +9717,7 @@ describe("CodeGenerator", () => {
|
|
|
9716
9717
|
it("should generate struct with array member", () => {
|
|
9717
9718
|
const source = `
|
|
9718
9719
|
struct Buffer {
|
|
9719
|
-
u8
|
|
9720
|
+
u8[64] data;
|
|
9720
9721
|
u32 size;
|
|
9721
9722
|
}
|
|
9722
9723
|
Buffer buf;
|
|
@@ -9833,7 +9834,7 @@ describe("CodeGenerator", () => {
|
|
|
9833
9834
|
it("should generate array with initializer list", () => {
|
|
9834
9835
|
const source = `
|
|
9835
9836
|
void test() {
|
|
9836
|
-
u32
|
|
9837
|
+
u32[5] arr <- [1, 2, 3, 4, 5];
|
|
9837
9838
|
}
|
|
9838
9839
|
`;
|
|
9839
9840
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -11130,7 +11131,7 @@ describe("CodeGenerator", () => {
|
|
|
11130
11131
|
data[0] <- 1;
|
|
11131
11132
|
}
|
|
11132
11133
|
void test() {
|
|
11133
|
-
u32
|
|
11134
|
+
u32[10] arr;
|
|
11134
11135
|
process(arr);
|
|
11135
11136
|
}
|
|
11136
11137
|
`;
|
|
@@ -11436,7 +11437,7 @@ describe("CodeGenerator", () => {
|
|
|
11436
11437
|
it("should handle array with size in declaration", () => {
|
|
11437
11438
|
const source = `
|
|
11438
11439
|
void test() {
|
|
11439
|
-
u8
|
|
11440
|
+
u8[10] buffer;
|
|
11440
11441
|
buffer[0] <- 0xFF;
|
|
11441
11442
|
}
|
|
11442
11443
|
`;
|
|
@@ -11458,7 +11459,7 @@ describe("CodeGenerator", () => {
|
|
|
11458
11459
|
const source = `
|
|
11459
11460
|
const u32 BUFFER_SIZE <- 64;
|
|
11460
11461
|
void test() {
|
|
11461
|
-
u8
|
|
11462
|
+
u8[BUFFER_SIZE] buffer;
|
|
11462
11463
|
buffer[0] <- 0;
|
|
11463
11464
|
}
|
|
11464
11465
|
`;
|
|
@@ -11475,6 +11476,90 @@ describe("CodeGenerator", () => {
|
|
|
11475
11476
|
|
|
11476
11477
|
expect(code).toContain("BUFFER_SIZE");
|
|
11477
11478
|
});
|
|
11479
|
+
|
|
11480
|
+
it("should reject C-style array declaration for primitive types", () => {
|
|
11481
|
+
const source = `
|
|
11482
|
+
void test() {
|
|
11483
|
+
u8 buffer[10];
|
|
11484
|
+
}
|
|
11485
|
+
`;
|
|
11486
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
11487
|
+
const generator = new CodeGenerator();
|
|
11488
|
+
const symbolTable = new SymbolTable();
|
|
11489
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
11490
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
11491
|
+
|
|
11492
|
+
expect(() => {
|
|
11493
|
+
generator.generate(tree, symbolTable, tokenStream, {
|
|
11494
|
+
symbolInfo: symbols,
|
|
11495
|
+
sourcePath: "test.cnx",
|
|
11496
|
+
});
|
|
11497
|
+
}).toThrow(/C-style array declaration is not allowed/);
|
|
11498
|
+
});
|
|
11499
|
+
|
|
11500
|
+
it("should reject C-style array declaration for user types", () => {
|
|
11501
|
+
const source = `
|
|
11502
|
+
struct Data {
|
|
11503
|
+
u32 value;
|
|
11504
|
+
}
|
|
11505
|
+
void test() {
|
|
11506
|
+
Data items[5];
|
|
11507
|
+
}
|
|
11508
|
+
`;
|
|
11509
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
11510
|
+
const generator = new CodeGenerator();
|
|
11511
|
+
const symbolTable = new SymbolTable();
|
|
11512
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
11513
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
11514
|
+
|
|
11515
|
+
expect(() => {
|
|
11516
|
+
generator.generate(tree, symbolTable, tokenStream, {
|
|
11517
|
+
symbolInfo: symbols,
|
|
11518
|
+
sourcePath: "test.cnx",
|
|
11519
|
+
});
|
|
11520
|
+
}).toThrow(/C-style array declaration is not allowed/);
|
|
11521
|
+
});
|
|
11522
|
+
|
|
11523
|
+
it("should allow empty brackets for size inference", () => {
|
|
11524
|
+
const source = `
|
|
11525
|
+
void test() {
|
|
11526
|
+
u8 data[] <- [1, 2, 3];
|
|
11527
|
+
}
|
|
11528
|
+
`;
|
|
11529
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
11530
|
+
const generator = new CodeGenerator();
|
|
11531
|
+
const symbolTable = new SymbolTable();
|
|
11532
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
11533
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
11534
|
+
|
|
11535
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
11536
|
+
symbolInfo: symbols,
|
|
11537
|
+
sourcePath: "test.cnx",
|
|
11538
|
+
});
|
|
11539
|
+
|
|
11540
|
+
expect(code).toContain("uint8_t data[3]");
|
|
11541
|
+
});
|
|
11542
|
+
|
|
11543
|
+
it("should allow multi-dimensional C-style arrays", () => {
|
|
11544
|
+
const source = `
|
|
11545
|
+
void test() {
|
|
11546
|
+
u8 matrix[4][4];
|
|
11547
|
+
matrix[0][0] <- 0;
|
|
11548
|
+
}
|
|
11549
|
+
`;
|
|
11550
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
11551
|
+
const generator = new CodeGenerator();
|
|
11552
|
+
const symbolTable = new SymbolTable();
|
|
11553
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
11554
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
11555
|
+
|
|
11556
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
11557
|
+
symbolInfo: symbols,
|
|
11558
|
+
sourcePath: "test.cnx",
|
|
11559
|
+
});
|
|
11560
|
+
|
|
11561
|
+
expect(code).toContain("uint8_t matrix[4][4]");
|
|
11562
|
+
});
|
|
11478
11563
|
});
|
|
11479
11564
|
|
|
11480
11565
|
describe("string expression type checking", () => {
|
|
@@ -12208,7 +12293,7 @@ describe("CodeGenerator", () => {
|
|
|
12208
12293
|
it("should handle sizeof on local array", () => {
|
|
12209
12294
|
const source = `
|
|
12210
12295
|
void test() {
|
|
12211
|
-
u8
|
|
12296
|
+
u8[16] buffer;
|
|
12212
12297
|
u32 size <- sizeof(buffer);
|
|
12213
12298
|
}
|
|
12214
12299
|
`;
|
|
@@ -12368,7 +12453,7 @@ describe("CodeGenerator", () => {
|
|
|
12368
12453
|
it("should resolve array element access in expression", () => {
|
|
12369
12454
|
const source = `
|
|
12370
12455
|
void test() {
|
|
12371
|
-
u32
|
|
12456
|
+
u32[10] arr;
|
|
12372
12457
|
u32 val <- arr[5];
|
|
12373
12458
|
}
|
|
12374
12459
|
`;
|
|
@@ -12389,7 +12474,7 @@ describe("CodeGenerator", () => {
|
|
|
12389
12474
|
it("should resolve array element with variable index", () => {
|
|
12390
12475
|
const source = `
|
|
12391
12476
|
void test() {
|
|
12392
|
-
u32
|
|
12477
|
+
u32[10] arr;
|
|
12393
12478
|
u32 i <- 3;
|
|
12394
12479
|
u32 val <- arr[i];
|
|
12395
12480
|
}
|
|
@@ -12802,7 +12887,7 @@ describe("CodeGenerator", () => {
|
|
|
12802
12887
|
const source = `
|
|
12803
12888
|
struct Item { u32 id; }
|
|
12804
12889
|
void test() {
|
|
12805
|
-
Item
|
|
12890
|
+
Item[10] items;
|
|
12806
12891
|
items[0].id <- 42;
|
|
12807
12892
|
}
|
|
12808
12893
|
`;
|
|
@@ -13071,7 +13156,7 @@ describe("CodeGenerator", () => {
|
|
|
13071
13156
|
it("should handle array type with user-defined struct", () => {
|
|
13072
13157
|
const source = `
|
|
13073
13158
|
struct Point { i32 x; i32 y; }
|
|
13074
|
-
Point
|
|
13159
|
+
Point[10] points;
|
|
13075
13160
|
`;
|
|
13076
13161
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13077
13162
|
const generator = new CodeGenerator();
|
|
@@ -13089,8 +13174,8 @@ describe("CodeGenerator", () => {
|
|
|
13089
13174
|
|
|
13090
13175
|
it("should handle array type with primitive types", () => {
|
|
13091
13176
|
const source = `
|
|
13092
|
-
u8
|
|
13093
|
-
i32
|
|
13177
|
+
u8[256] buffer;
|
|
13178
|
+
i32[10] numbers;
|
|
13094
13179
|
`;
|
|
13095
13180
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13096
13181
|
const generator = new CodeGenerator();
|
|
@@ -13590,7 +13675,7 @@ describe("CodeGenerator", () => {
|
|
|
13590
13675
|
describe("array access resolution helpers", () => {
|
|
13591
13676
|
it("should resolve simple array access", () => {
|
|
13592
13677
|
const source = `
|
|
13593
|
-
u8
|
|
13678
|
+
u8[10] buffer;
|
|
13594
13679
|
void test() {
|
|
13595
13680
|
u8 val <- buffer[5];
|
|
13596
13681
|
}
|
|
@@ -13611,7 +13696,7 @@ describe("CodeGenerator", () => {
|
|
|
13611
13696
|
|
|
13612
13697
|
it("should handle array access with variable index", () => {
|
|
13613
13698
|
const source = `
|
|
13614
|
-
u8
|
|
13699
|
+
u8[20] data;
|
|
13615
13700
|
void test() {
|
|
13616
13701
|
u32 idx <- 3;
|
|
13617
13702
|
u8 val <- data[idx];
|
|
@@ -13780,6 +13865,1951 @@ describe("CodeGenerator", () => {
|
|
|
13780
13865
|
|
|
13781
13866
|
expect(code).toContain("strlen");
|
|
13782
13867
|
});
|
|
13868
|
+
|
|
13869
|
+
it("should generate cache for multiple .length accesses", () => {
|
|
13870
|
+
const source = `
|
|
13871
|
+
void test() {
|
|
13872
|
+
string<32> s <- "hello";
|
|
13873
|
+
if (s.length > 0) {
|
|
13874
|
+
u32 x <- s.length;
|
|
13875
|
+
}
|
|
13876
|
+
}
|
|
13877
|
+
`;
|
|
13878
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13879
|
+
const generator = new CodeGenerator();
|
|
13880
|
+
const symbolTable = new SymbolTable();
|
|
13881
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
13882
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
13883
|
+
|
|
13884
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
13885
|
+
symbolInfo: symbols,
|
|
13886
|
+
sourcePath: "test.cnx",
|
|
13887
|
+
});
|
|
13888
|
+
|
|
13889
|
+
// Should generate cache variable for repeated .length access
|
|
13890
|
+
expect(code).toContain("strlen");
|
|
13891
|
+
});
|
|
13892
|
+
|
|
13893
|
+
it("should not cache for single .length access", () => {
|
|
13894
|
+
const source = `
|
|
13895
|
+
void test() {
|
|
13896
|
+
string<32> s <- "hello";
|
|
13897
|
+
u32 len <- s.length;
|
|
13898
|
+
}
|
|
13899
|
+
`;
|
|
13900
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13901
|
+
const generator = new CodeGenerator();
|
|
13902
|
+
const symbolTable = new SymbolTable();
|
|
13903
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
13904
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
13905
|
+
|
|
13906
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
13907
|
+
symbolInfo: symbols,
|
|
13908
|
+
sourcePath: "test.cnx",
|
|
13909
|
+
});
|
|
13910
|
+
|
|
13911
|
+
// Single access should just use strlen directly
|
|
13912
|
+
expect(code).toContain("strlen(s)");
|
|
13913
|
+
});
|
|
13914
|
+
});
|
|
13915
|
+
|
|
13916
|
+
describe("scope-qualified function resolution", () => {
|
|
13917
|
+
it("should resolve this.method() to CurrentScope_method", () => {
|
|
13918
|
+
const source = `
|
|
13919
|
+
enum MyEnum { A, B }
|
|
13920
|
+
scope Foo {
|
|
13921
|
+
public MyEnum bar() {
|
|
13922
|
+
return global.MyEnum.A;
|
|
13923
|
+
}
|
|
13924
|
+
public void test() {
|
|
13925
|
+
MyEnum result <- this.bar();
|
|
13926
|
+
}
|
|
13927
|
+
}
|
|
13928
|
+
`;
|
|
13929
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13930
|
+
const generator = new CodeGenerator();
|
|
13931
|
+
const symbolTable = new SymbolTable();
|
|
13932
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
13933
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
13934
|
+
|
|
13935
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
13936
|
+
symbolInfo: symbols,
|
|
13937
|
+
sourcePath: "test.cnx",
|
|
13938
|
+
});
|
|
13939
|
+
|
|
13940
|
+
// this.bar() should become Foo_bar()
|
|
13941
|
+
expect(code).toContain("Foo_bar()");
|
|
13942
|
+
});
|
|
13943
|
+
|
|
13944
|
+
it("should resolve Scope.method() to Scope_method", () => {
|
|
13945
|
+
const source = `
|
|
13946
|
+
scope Utils {
|
|
13947
|
+
public u32 getValue() {
|
|
13948
|
+
return 42;
|
|
13949
|
+
}
|
|
13950
|
+
}
|
|
13951
|
+
void main() {
|
|
13952
|
+
u32 v <- Utils.getValue();
|
|
13953
|
+
}
|
|
13954
|
+
`;
|
|
13955
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13956
|
+
const generator = new CodeGenerator();
|
|
13957
|
+
const symbolTable = new SymbolTable();
|
|
13958
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
13959
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
13960
|
+
|
|
13961
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
13962
|
+
symbolInfo: symbols,
|
|
13963
|
+
sourcePath: "test.cnx",
|
|
13964
|
+
});
|
|
13965
|
+
|
|
13966
|
+
expect(code).toContain("Utils_getValue()");
|
|
13967
|
+
});
|
|
13968
|
+
|
|
13969
|
+
it("should resolve global.func() to func", () => {
|
|
13970
|
+
const source = `
|
|
13971
|
+
u32 getNumber() {
|
|
13972
|
+
return 100;
|
|
13973
|
+
}
|
|
13974
|
+
scope Test {
|
|
13975
|
+
public void run() {
|
|
13976
|
+
u32 n <- global.getNumber();
|
|
13977
|
+
}
|
|
13978
|
+
}
|
|
13979
|
+
`;
|
|
13980
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
13981
|
+
const generator = new CodeGenerator();
|
|
13982
|
+
const symbolTable = new SymbolTable();
|
|
13983
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
13984
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
13985
|
+
|
|
13986
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
13987
|
+
symbolInfo: symbols,
|
|
13988
|
+
sourcePath: "test.cnx",
|
|
13989
|
+
});
|
|
13990
|
+
|
|
13991
|
+
// global.getNumber() should become getNumber()
|
|
13992
|
+
expect(code).toContain("getNumber()");
|
|
13993
|
+
});
|
|
13994
|
+
|
|
13995
|
+
it("should resolve global.Scope.method() to Scope_method", () => {
|
|
13996
|
+
const source = `
|
|
13997
|
+
scope Config {
|
|
13998
|
+
public u32 getTimeout() {
|
|
13999
|
+
return 1000;
|
|
14000
|
+
}
|
|
14001
|
+
}
|
|
14002
|
+
scope Other {
|
|
14003
|
+
public void test() {
|
|
14004
|
+
u32 t <- global.Config.getTimeout();
|
|
14005
|
+
}
|
|
14006
|
+
}
|
|
14007
|
+
`;
|
|
14008
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14009
|
+
const generator = new CodeGenerator();
|
|
14010
|
+
const symbolTable = new SymbolTable();
|
|
14011
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14012
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14013
|
+
|
|
14014
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14015
|
+
symbolInfo: symbols,
|
|
14016
|
+
sourcePath: "test.cnx",
|
|
14017
|
+
});
|
|
14018
|
+
|
|
14019
|
+
// global.Config.getTimeout() should become Config_getTimeout()
|
|
14020
|
+
expect(code).toContain("Config_getTimeout()");
|
|
14021
|
+
});
|
|
14022
|
+
});
|
|
14023
|
+
|
|
14024
|
+
describe("expression type checking", () => {
|
|
14025
|
+
it("should detect decimal integer literal in comparison", () => {
|
|
14026
|
+
const source = `
|
|
14027
|
+
void test() {
|
|
14028
|
+
u32 val <- 5;
|
|
14029
|
+
bool isZero <- (val = 0);
|
|
14030
|
+
}
|
|
14031
|
+
`;
|
|
14032
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14033
|
+
const generator = new CodeGenerator();
|
|
14034
|
+
const symbolTable = new SymbolTable();
|
|
14035
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14036
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14037
|
+
|
|
14038
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14039
|
+
symbolInfo: symbols,
|
|
14040
|
+
sourcePath: "test.cnx",
|
|
14041
|
+
});
|
|
14042
|
+
|
|
14043
|
+
// Integer comparison should work
|
|
14044
|
+
expect(code).toContain("val == 0");
|
|
14045
|
+
});
|
|
14046
|
+
|
|
14047
|
+
it("should detect hex literal in comparison", () => {
|
|
14048
|
+
const source = `
|
|
14049
|
+
void test() {
|
|
14050
|
+
u32 flags <- 0;
|
|
14051
|
+
bool check <- (flags = 0xFF);
|
|
14052
|
+
}
|
|
14053
|
+
`;
|
|
14054
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14055
|
+
const generator = new CodeGenerator();
|
|
14056
|
+
const symbolTable = new SymbolTable();
|
|
14057
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14058
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14059
|
+
|
|
14060
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14061
|
+
symbolInfo: symbols,
|
|
14062
|
+
sourcePath: "test.cnx",
|
|
14063
|
+
});
|
|
14064
|
+
|
|
14065
|
+
expect(code).toContain("0xFF");
|
|
14066
|
+
});
|
|
14067
|
+
|
|
14068
|
+
it("should detect binary literal in comparison", () => {
|
|
14069
|
+
const source = `
|
|
14070
|
+
void test() {
|
|
14071
|
+
u32 mode <- 0;
|
|
14072
|
+
bool check <- (mode = 0b1010);
|
|
14073
|
+
}
|
|
14074
|
+
`;
|
|
14075
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14076
|
+
const generator = new CodeGenerator();
|
|
14077
|
+
const symbolTable = new SymbolTable();
|
|
14078
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14079
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14080
|
+
|
|
14081
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14082
|
+
symbolInfo: symbols,
|
|
14083
|
+
sourcePath: "test.cnx",
|
|
14084
|
+
});
|
|
14085
|
+
|
|
14086
|
+
// Binary literal passes through (not converted)
|
|
14087
|
+
expect(code).toContain("0b1010");
|
|
14088
|
+
});
|
|
14089
|
+
|
|
14090
|
+
it("should detect variable of integer type", () => {
|
|
14091
|
+
const source = `
|
|
14092
|
+
void test() {
|
|
14093
|
+
i32 code <- 5;
|
|
14094
|
+
i32 limit <- 10;
|
|
14095
|
+
bool check <- (code = limit);
|
|
14096
|
+
}
|
|
14097
|
+
`;
|
|
14098
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14099
|
+
const generator = new CodeGenerator();
|
|
14100
|
+
const symbolTable = new SymbolTable();
|
|
14101
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14102
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14103
|
+
|
|
14104
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14105
|
+
symbolInfo: symbols,
|
|
14106
|
+
sourcePath: "test.cnx",
|
|
14107
|
+
});
|
|
14108
|
+
|
|
14109
|
+
// Comparison with integer variable
|
|
14110
|
+
expect(code).toContain("code == limit");
|
|
14111
|
+
});
|
|
14112
|
+
|
|
14113
|
+
it("should detect string literal", () => {
|
|
14114
|
+
const source = `
|
|
14115
|
+
void test() {
|
|
14116
|
+
string<32> s <- "hello";
|
|
14117
|
+
bool check <- (s = "hello");
|
|
14118
|
+
}
|
|
14119
|
+
`;
|
|
14120
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14121
|
+
const generator = new CodeGenerator();
|
|
14122
|
+
const symbolTable = new SymbolTable();
|
|
14123
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14124
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14125
|
+
|
|
14126
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14127
|
+
symbolInfo: symbols,
|
|
14128
|
+
sourcePath: "test.cnx",
|
|
14129
|
+
});
|
|
14130
|
+
|
|
14131
|
+
// String comparison should use strcmp
|
|
14132
|
+
expect(code).toContain("strcmp");
|
|
14133
|
+
});
|
|
14134
|
+
|
|
14135
|
+
it("should detect string variable", () => {
|
|
14136
|
+
const source = `
|
|
14137
|
+
void test() {
|
|
14138
|
+
string<32> s1 <- "hello";
|
|
14139
|
+
string<32> s2 <- "world";
|
|
14140
|
+
bool check <- (s1 = s2);
|
|
14141
|
+
}
|
|
14142
|
+
`;
|
|
14143
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14144
|
+
const generator = new CodeGenerator();
|
|
14145
|
+
const symbolTable = new SymbolTable();
|
|
14146
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14147
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14148
|
+
|
|
14149
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14150
|
+
symbolInfo: symbols,
|
|
14151
|
+
sourcePath: "test.cnx",
|
|
14152
|
+
});
|
|
14153
|
+
|
|
14154
|
+
// String comparison should use strcmp
|
|
14155
|
+
expect(code).toContain("strcmp");
|
|
14156
|
+
});
|
|
14157
|
+
|
|
14158
|
+
it("should detect array element of string array", () => {
|
|
14159
|
+
const source = `
|
|
14160
|
+
void test() {
|
|
14161
|
+
string<32> names[3];
|
|
14162
|
+
names[0] <- "Alice";
|
|
14163
|
+
bool check <- (names[0] = "Alice");
|
|
14164
|
+
}
|
|
14165
|
+
`;
|
|
14166
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14167
|
+
const generator = new CodeGenerator();
|
|
14168
|
+
const symbolTable = new SymbolTable();
|
|
14169
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14170
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14171
|
+
|
|
14172
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14173
|
+
symbolInfo: symbols,
|
|
14174
|
+
sourcePath: "test.cnx",
|
|
14175
|
+
});
|
|
14176
|
+
|
|
14177
|
+
// String array element comparison should use strcmp
|
|
14178
|
+
expect(code).toContain("strcmp");
|
|
14179
|
+
});
|
|
14180
|
+
});
|
|
14181
|
+
|
|
14182
|
+
describe("array type registration", () => {
|
|
14183
|
+
it("should register array type correctly", () => {
|
|
14184
|
+
const source = `
|
|
14185
|
+
void test() {
|
|
14186
|
+
u8[10] buffer;
|
|
14187
|
+
buffer[0] <- 0xFF;
|
|
14188
|
+
}
|
|
14189
|
+
`;
|
|
14190
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14191
|
+
const generator = new CodeGenerator();
|
|
14192
|
+
const symbolTable = new SymbolTable();
|
|
14193
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14194
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14195
|
+
|
|
14196
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14197
|
+
symbolInfo: symbols,
|
|
14198
|
+
sourcePath: "test.cnx",
|
|
14199
|
+
});
|
|
14200
|
+
|
|
14201
|
+
expect(code).toContain("uint8_t buffer[10]");
|
|
14202
|
+
});
|
|
14203
|
+
|
|
14204
|
+
it("should register multi-dimensional array", () => {
|
|
14205
|
+
const source = `
|
|
14206
|
+
void test() {
|
|
14207
|
+
u8[10] matrix[20];
|
|
14208
|
+
matrix[0][0] <- 0;
|
|
14209
|
+
}
|
|
14210
|
+
`;
|
|
14211
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14212
|
+
const generator = new CodeGenerator();
|
|
14213
|
+
const symbolTable = new SymbolTable();
|
|
14214
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14215
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14216
|
+
|
|
14217
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14218
|
+
symbolInfo: symbols,
|
|
14219
|
+
sourcePath: "test.cnx",
|
|
14220
|
+
});
|
|
14221
|
+
|
|
14222
|
+
expect(code).toContain("uint8_t matrix[10][20]");
|
|
14223
|
+
});
|
|
14224
|
+
|
|
14225
|
+
it("should handle i32 array type", () => {
|
|
14226
|
+
const source = `
|
|
14227
|
+
void test() {
|
|
14228
|
+
i32[5] values;
|
|
14229
|
+
values[0] <- 100;
|
|
14230
|
+
}
|
|
14231
|
+
`;
|
|
14232
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14233
|
+
const generator = new CodeGenerator();
|
|
14234
|
+
const symbolTable = new SymbolTable();
|
|
14235
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14236
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14237
|
+
|
|
14238
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14239
|
+
symbolInfo: symbols,
|
|
14240
|
+
sourcePath: "test.cnx",
|
|
14241
|
+
});
|
|
14242
|
+
|
|
14243
|
+
expect(code).toContain("int32_t values[5]");
|
|
14244
|
+
});
|
|
14245
|
+
|
|
14246
|
+
it("should handle const array type", () => {
|
|
14247
|
+
const source = `
|
|
14248
|
+
void test() {
|
|
14249
|
+
const u8[4] data <- [1, 2, 3, 4];
|
|
14250
|
+
}
|
|
14251
|
+
`;
|
|
14252
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14253
|
+
const generator = new CodeGenerator();
|
|
14254
|
+
const symbolTable = new SymbolTable();
|
|
14255
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14256
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14257
|
+
|
|
14258
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14259
|
+
symbolInfo: symbols,
|
|
14260
|
+
sourcePath: "test.cnx",
|
|
14261
|
+
});
|
|
14262
|
+
|
|
14263
|
+
expect(code).toContain("const uint8_t data[4]");
|
|
14264
|
+
});
|
|
14265
|
+
|
|
14266
|
+
it("should handle struct array with C-Next syntax", () => {
|
|
14267
|
+
const source = `
|
|
14268
|
+
struct Point {
|
|
14269
|
+
i32 x;
|
|
14270
|
+
i32 y;
|
|
14271
|
+
}
|
|
14272
|
+
void test() {
|
|
14273
|
+
Point[3] points;
|
|
14274
|
+
points[0].x <- 10;
|
|
14275
|
+
}
|
|
14276
|
+
`;
|
|
14277
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14278
|
+
const generator = new CodeGenerator();
|
|
14279
|
+
const symbolTable = new SymbolTable();
|
|
14280
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14281
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14282
|
+
|
|
14283
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14284
|
+
symbolInfo: symbols,
|
|
14285
|
+
sourcePath: "test.cnx",
|
|
14286
|
+
});
|
|
14287
|
+
|
|
14288
|
+
expect(code).toContain("Point points[3]");
|
|
14289
|
+
expect(code).toContain("points[0].x = 10");
|
|
14290
|
+
});
|
|
14291
|
+
|
|
14292
|
+
it("should handle bitmap array with C-Next syntax", () => {
|
|
14293
|
+
const source = `
|
|
14294
|
+
bitmap8 Flags {
|
|
14295
|
+
active,
|
|
14296
|
+
ready,
|
|
14297
|
+
error,
|
|
14298
|
+
mode[3],
|
|
14299
|
+
priority[2]
|
|
14300
|
+
}
|
|
14301
|
+
void test() {
|
|
14302
|
+
Flags[4] flags;
|
|
14303
|
+
flags[0].active <- true;
|
|
14304
|
+
}
|
|
14305
|
+
`;
|
|
14306
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14307
|
+
const generator = new CodeGenerator();
|
|
14308
|
+
const symbolTable = new SymbolTable();
|
|
14309
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14310
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14311
|
+
|
|
14312
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14313
|
+
symbolInfo: symbols,
|
|
14314
|
+
sourcePath: "test.cnx",
|
|
14315
|
+
});
|
|
14316
|
+
|
|
14317
|
+
expect(code).toContain("Flags flags[4]");
|
|
14318
|
+
// Bitmap field access should generate bit manipulation
|
|
14319
|
+
expect(code).toContain("flags[0]");
|
|
14320
|
+
});
|
|
14321
|
+
|
|
14322
|
+
it("should handle enum array with C-Next syntax", () => {
|
|
14323
|
+
const source = `
|
|
14324
|
+
enum Color {
|
|
14325
|
+
RED,
|
|
14326
|
+
GREEN,
|
|
14327
|
+
BLUE
|
|
14328
|
+
}
|
|
14329
|
+
void test() {
|
|
14330
|
+
Color[3] colors;
|
|
14331
|
+
colors[0] <- RED;
|
|
14332
|
+
}
|
|
14333
|
+
`;
|
|
14334
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14335
|
+
const generator = new CodeGenerator();
|
|
14336
|
+
const symbolTable = new SymbolTable();
|
|
14337
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14338
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14339
|
+
|
|
14340
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14341
|
+
symbolInfo: symbols,
|
|
14342
|
+
sourcePath: "test.cnx",
|
|
14343
|
+
});
|
|
14344
|
+
|
|
14345
|
+
expect(code).toContain("Color colors[3]");
|
|
14346
|
+
expect(code).toContain("colors[0] = Color_RED");
|
|
14347
|
+
});
|
|
14348
|
+
|
|
14349
|
+
it("should handle struct array initializer with C-Next syntax", () => {
|
|
14350
|
+
const source = `
|
|
14351
|
+
struct Item {
|
|
14352
|
+
u8 id;
|
|
14353
|
+
}
|
|
14354
|
+
const Item[2] items <- [{id: 1}, {id: 2}];
|
|
14355
|
+
`;
|
|
14356
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14357
|
+
const generator = new CodeGenerator();
|
|
14358
|
+
const symbolTable = new SymbolTable();
|
|
14359
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14360
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14361
|
+
|
|
14362
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14363
|
+
symbolInfo: symbols,
|
|
14364
|
+
sourcePath: "test.cnx",
|
|
14365
|
+
});
|
|
14366
|
+
|
|
14367
|
+
expect(code).toContain("const Item items[2]");
|
|
14368
|
+
expect(code).toContain(".id = 1");
|
|
14369
|
+
});
|
|
14370
|
+
});
|
|
14371
|
+
|
|
14372
|
+
describe("C++ mode member conversion", () => {
|
|
14373
|
+
it("should detect parameter member access needing conversion", () => {
|
|
14374
|
+
const source = `
|
|
14375
|
+
struct Config {
|
|
14376
|
+
u32 value;
|
|
14377
|
+
}
|
|
14378
|
+
void process(Config cfg) {
|
|
14379
|
+
u32 v <- cfg.value;
|
|
14380
|
+
}
|
|
14381
|
+
`;
|
|
14382
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14383
|
+
const generator = new CodeGenerator();
|
|
14384
|
+
const symbolTable = new SymbolTable();
|
|
14385
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14386
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14387
|
+
|
|
14388
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14389
|
+
symbolInfo: symbols,
|
|
14390
|
+
sourcePath: "test.cnx",
|
|
14391
|
+
cppMode: true,
|
|
14392
|
+
});
|
|
14393
|
+
|
|
14394
|
+
// In C++ mode, struct params use . not ->
|
|
14395
|
+
expect(code).toContain("cfg.value");
|
|
14396
|
+
});
|
|
14397
|
+
|
|
14398
|
+
it("should detect array element member access", () => {
|
|
14399
|
+
const source = `
|
|
14400
|
+
struct Item {
|
|
14401
|
+
u32 id;
|
|
14402
|
+
}
|
|
14403
|
+
void process() {
|
|
14404
|
+
Item[3] items;
|
|
14405
|
+
u32 first <- items[0].id;
|
|
14406
|
+
}
|
|
14407
|
+
`;
|
|
14408
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14409
|
+
const generator = new CodeGenerator();
|
|
14410
|
+
const symbolTable = new SymbolTable();
|
|
14411
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14412
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14413
|
+
|
|
14414
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14415
|
+
symbolInfo: symbols,
|
|
14416
|
+
sourcePath: "test.cnx",
|
|
14417
|
+
cppMode: true,
|
|
14418
|
+
});
|
|
14419
|
+
|
|
14420
|
+
// Array element member access
|
|
14421
|
+
expect(code).toContain("items[0].id");
|
|
14422
|
+
});
|
|
14423
|
+
|
|
14424
|
+
it("should use arrow for struct params in C mode", () => {
|
|
14425
|
+
const source = `
|
|
14426
|
+
struct Config {
|
|
14427
|
+
u32 value;
|
|
14428
|
+
}
|
|
14429
|
+
void process(Config cfg) {
|
|
14430
|
+
u32 v <- cfg.value;
|
|
14431
|
+
}
|
|
14432
|
+
`;
|
|
14433
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14434
|
+
const generator = new CodeGenerator();
|
|
14435
|
+
const symbolTable = new SymbolTable();
|
|
14436
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14437
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14438
|
+
|
|
14439
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14440
|
+
symbolInfo: symbols,
|
|
14441
|
+
sourcePath: "test.cnx",
|
|
14442
|
+
cppMode: false,
|
|
14443
|
+
});
|
|
14444
|
+
|
|
14445
|
+
// In C mode, struct params use ->
|
|
14446
|
+
expect(code).toContain("cfg->value");
|
|
14447
|
+
});
|
|
14448
|
+
|
|
14449
|
+
it("should handle const struct parameters in C++ mode", () => {
|
|
14450
|
+
const source = `
|
|
14451
|
+
struct Settings {
|
|
14452
|
+
u32 timeout;
|
|
14453
|
+
bool enabled;
|
|
14454
|
+
}
|
|
14455
|
+
void configure(const Settings s) {
|
|
14456
|
+
u32 t <- s.timeout;
|
|
14457
|
+
}
|
|
14458
|
+
`;
|
|
14459
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14460
|
+
const generator = new CodeGenerator();
|
|
14461
|
+
const symbolTable = new SymbolTable();
|
|
14462
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14463
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14464
|
+
|
|
14465
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14466
|
+
symbolInfo: symbols,
|
|
14467
|
+
sourcePath: "test.cnx",
|
|
14468
|
+
cppMode: true,
|
|
14469
|
+
});
|
|
14470
|
+
|
|
14471
|
+
// Const struct param should use reference in C++
|
|
14472
|
+
expect(code).toContain("const Settings&");
|
|
14473
|
+
expect(code).toContain("s.timeout");
|
|
14474
|
+
});
|
|
14475
|
+
});
|
|
14476
|
+
|
|
14477
|
+
describe("struct generation", () => {
|
|
14478
|
+
it("should generate struct typedef", () => {
|
|
14479
|
+
const source = `
|
|
14480
|
+
struct Point {
|
|
14481
|
+
i32 x;
|
|
14482
|
+
i32 y;
|
|
14483
|
+
}
|
|
14484
|
+
`;
|
|
14485
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14486
|
+
const generator = new CodeGenerator();
|
|
14487
|
+
const symbolTable = new SymbolTable();
|
|
14488
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14489
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14490
|
+
|
|
14491
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14492
|
+
symbolInfo: symbols,
|
|
14493
|
+
sourcePath: "test.cnx",
|
|
14494
|
+
});
|
|
14495
|
+
|
|
14496
|
+
expect(code).toContain("typedef struct");
|
|
14497
|
+
expect(code).toContain("int32_t x");
|
|
14498
|
+
expect(code).toContain("int32_t y");
|
|
14499
|
+
expect(code).toContain("} Point;");
|
|
14500
|
+
});
|
|
14501
|
+
|
|
14502
|
+
it("should generate struct with callback field", () => {
|
|
14503
|
+
const source = `
|
|
14504
|
+
void handler(u32 val) {}
|
|
14505
|
+
struct Button {
|
|
14506
|
+
u32 id;
|
|
14507
|
+
handler onClick;
|
|
14508
|
+
}
|
|
14509
|
+
`;
|
|
14510
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14511
|
+
const generator = new CodeGenerator();
|
|
14512
|
+
const symbolTable = new SymbolTable();
|
|
14513
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14514
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14515
|
+
|
|
14516
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14517
|
+
symbolInfo: symbols,
|
|
14518
|
+
sourcePath: "test.cnx",
|
|
14519
|
+
});
|
|
14520
|
+
|
|
14521
|
+
expect(code).toContain("typedef struct");
|
|
14522
|
+
expect(code).toContain("} Button;");
|
|
14523
|
+
});
|
|
14524
|
+
});
|
|
14525
|
+
|
|
14526
|
+
describe("register generation", () => {
|
|
14527
|
+
it("should generate register comment", () => {
|
|
14528
|
+
const source = `
|
|
14529
|
+
register GPIO7 @ 0x42004000 {
|
|
14530
|
+
rw u32 DR @ 0x00;
|
|
14531
|
+
}
|
|
14532
|
+
`;
|
|
14533
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14534
|
+
const generator = new CodeGenerator();
|
|
14535
|
+
const symbolTable = new SymbolTable();
|
|
14536
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14537
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14538
|
+
|
|
14539
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14540
|
+
symbolInfo: symbols,
|
|
14541
|
+
sourcePath: "test.cnx",
|
|
14542
|
+
});
|
|
14543
|
+
|
|
14544
|
+
// Register declaration generates a comment
|
|
14545
|
+
expect(code).toContain("Register: GPIO7");
|
|
14546
|
+
});
|
|
14547
|
+
|
|
14548
|
+
it("should generate scoped register comment", () => {
|
|
14549
|
+
const source = `
|
|
14550
|
+
scope Teensy4 {
|
|
14551
|
+
register GPIO7 @ 0x42004000 {
|
|
14552
|
+
rw u32 DR @ 0x00;
|
|
14553
|
+
}
|
|
14554
|
+
}
|
|
14555
|
+
`;
|
|
14556
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14557
|
+
const generator = new CodeGenerator();
|
|
14558
|
+
const symbolTable = new SymbolTable();
|
|
14559
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14560
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14561
|
+
|
|
14562
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14563
|
+
symbolInfo: symbols,
|
|
14564
|
+
sourcePath: "test.cnx",
|
|
14565
|
+
});
|
|
14566
|
+
|
|
14567
|
+
// Scoped register should show scope in output
|
|
14568
|
+
expect(code).toContain("Teensy4");
|
|
14569
|
+
expect(code).toContain("GPIO7");
|
|
14570
|
+
});
|
|
14571
|
+
});
|
|
14572
|
+
|
|
14573
|
+
describe("enum type checking in expressions", () => {
|
|
14574
|
+
it("should handle enum variable assignment", () => {
|
|
14575
|
+
const source = `
|
|
14576
|
+
enum Color { RED, GREEN, BLUE }
|
|
14577
|
+
void test() {
|
|
14578
|
+
Color c <- Color.RED;
|
|
14579
|
+
c <- Color.GREEN;
|
|
14580
|
+
}
|
|
14581
|
+
`;
|
|
14582
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14583
|
+
const generator = new CodeGenerator();
|
|
14584
|
+
const symbolTable = new SymbolTable();
|
|
14585
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14586
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14587
|
+
|
|
14588
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14589
|
+
symbolInfo: symbols,
|
|
14590
|
+
sourcePath: "test.cnx",
|
|
14591
|
+
});
|
|
14592
|
+
|
|
14593
|
+
expect(code).toContain("Color_RED");
|
|
14594
|
+
expect(code).toContain("Color_GREEN");
|
|
14595
|
+
});
|
|
14596
|
+
|
|
14597
|
+
it("should handle enum comparison", () => {
|
|
14598
|
+
const source = `
|
|
14599
|
+
enum Status { OK, ERROR }
|
|
14600
|
+
void test() {
|
|
14601
|
+
Status s <- Status.OK;
|
|
14602
|
+
if (s = Status.ERROR) {
|
|
14603
|
+
s <- Status.OK;
|
|
14604
|
+
}
|
|
14605
|
+
}
|
|
14606
|
+
`;
|
|
14607
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14608
|
+
const generator = new CodeGenerator();
|
|
14609
|
+
const symbolTable = new SymbolTable();
|
|
14610
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14611
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14612
|
+
|
|
14613
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14614
|
+
symbolInfo: symbols,
|
|
14615
|
+
sourcePath: "test.cnx",
|
|
14616
|
+
});
|
|
14617
|
+
|
|
14618
|
+
expect(code).toContain("s == Status_ERROR");
|
|
14619
|
+
});
|
|
14620
|
+
|
|
14621
|
+
it("should handle enum return from function", () => {
|
|
14622
|
+
const source = `
|
|
14623
|
+
enum Result { SUCCESS, FAILURE }
|
|
14624
|
+
Result getResult() {
|
|
14625
|
+
return Result.SUCCESS;
|
|
14626
|
+
}
|
|
14627
|
+
void test() {
|
|
14628
|
+
Result r <- getResult();
|
|
14629
|
+
}
|
|
14630
|
+
`;
|
|
14631
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14632
|
+
const generator = new CodeGenerator();
|
|
14633
|
+
const symbolTable = new SymbolTable();
|
|
14634
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14635
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14636
|
+
|
|
14637
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14638
|
+
symbolInfo: symbols,
|
|
14639
|
+
sourcePath: "test.cnx",
|
|
14640
|
+
});
|
|
14641
|
+
|
|
14642
|
+
expect(code).toContain("Result_SUCCESS");
|
|
14643
|
+
expect(code).toContain("getResult()");
|
|
14644
|
+
});
|
|
14645
|
+
});
|
|
14646
|
+
|
|
14647
|
+
describe("overflow helpers", () => {
|
|
14648
|
+
it("should generate clamp add helper", () => {
|
|
14649
|
+
const source = `
|
|
14650
|
+
clamp u8 val <- 250;
|
|
14651
|
+
void test() {
|
|
14652
|
+
val +<- 10;
|
|
14653
|
+
}
|
|
14654
|
+
`;
|
|
14655
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14656
|
+
const generator = new CodeGenerator();
|
|
14657
|
+
const symbolTable = new SymbolTable();
|
|
14658
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14659
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14660
|
+
|
|
14661
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14662
|
+
symbolInfo: symbols,
|
|
14663
|
+
sourcePath: "test.cnx",
|
|
14664
|
+
});
|
|
14665
|
+
|
|
14666
|
+
expect(code).toContain("cnx_clamp_add_u8");
|
|
14667
|
+
});
|
|
14668
|
+
|
|
14669
|
+
it("should generate clamp sub helper", () => {
|
|
14670
|
+
const source = `
|
|
14671
|
+
clamp u8 val <- 5;
|
|
14672
|
+
void test() {
|
|
14673
|
+
val -<- 10;
|
|
14674
|
+
}
|
|
14675
|
+
`;
|
|
14676
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14677
|
+
const generator = new CodeGenerator();
|
|
14678
|
+
const symbolTable = new SymbolTable();
|
|
14679
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14680
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14681
|
+
|
|
14682
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14683
|
+
symbolInfo: symbols,
|
|
14684
|
+
sourcePath: "test.cnx",
|
|
14685
|
+
});
|
|
14686
|
+
|
|
14687
|
+
expect(code).toContain("cnx_clamp_sub_u8");
|
|
14688
|
+
});
|
|
14689
|
+
});
|
|
14690
|
+
|
|
14691
|
+
describe("preprocessor directives", () => {
|
|
14692
|
+
it("should handle C include directive", () => {
|
|
14693
|
+
const source = `
|
|
14694
|
+
#include <stdio.h>
|
|
14695
|
+
void test() {}
|
|
14696
|
+
`;
|
|
14697
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14698
|
+
const generator = new CodeGenerator();
|
|
14699
|
+
const symbolTable = new SymbolTable();
|
|
14700
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14701
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14702
|
+
|
|
14703
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14704
|
+
symbolInfo: symbols,
|
|
14705
|
+
sourcePath: "test.cnx",
|
|
14706
|
+
});
|
|
14707
|
+
|
|
14708
|
+
expect(code).toContain("#include <stdio.h>");
|
|
14709
|
+
});
|
|
14710
|
+
|
|
14711
|
+
it("should handle #define without value", () => {
|
|
14712
|
+
const source = `
|
|
14713
|
+
#define DEBUG_MODE
|
|
14714
|
+
void test() {}
|
|
14715
|
+
`;
|
|
14716
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14717
|
+
const generator = new CodeGenerator();
|
|
14718
|
+
const symbolTable = new SymbolTable();
|
|
14719
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14720
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14721
|
+
|
|
14722
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14723
|
+
symbolInfo: symbols,
|
|
14724
|
+
sourcePath: "test.cnx",
|
|
14725
|
+
});
|
|
14726
|
+
|
|
14727
|
+
expect(code).toContain("#define DEBUG_MODE");
|
|
14728
|
+
});
|
|
14729
|
+
});
|
|
14730
|
+
|
|
14731
|
+
describe("bit range access", () => {
|
|
14732
|
+
it("should generate bit range read expression", () => {
|
|
14733
|
+
const source = `
|
|
14734
|
+
void test() {
|
|
14735
|
+
u32 val <- 0xFF00;
|
|
14736
|
+
u8 byte <- val[8, 8];
|
|
14737
|
+
}
|
|
14738
|
+
`;
|
|
14739
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14740
|
+
const generator = new CodeGenerator();
|
|
14741
|
+
const symbolTable = new SymbolTable();
|
|
14742
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14743
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14744
|
+
|
|
14745
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14746
|
+
symbolInfo: symbols,
|
|
14747
|
+
sourcePath: "test.cnx",
|
|
14748
|
+
});
|
|
14749
|
+
|
|
14750
|
+
// Bit range access generates shift and mask
|
|
14751
|
+
expect(code).toContain(">> 8");
|
|
14752
|
+
expect(code).toContain("0xFF");
|
|
14753
|
+
});
|
|
14754
|
+
|
|
14755
|
+
it("should generate bit range write expression", () => {
|
|
14756
|
+
const source = `
|
|
14757
|
+
void test() {
|
|
14758
|
+
u32 val <- 0;
|
|
14759
|
+
val[8, 8] <- 0xFF;
|
|
14760
|
+
}
|
|
14761
|
+
`;
|
|
14762
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14763
|
+
const generator = new CodeGenerator();
|
|
14764
|
+
const symbolTable = new SymbolTable();
|
|
14765
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14766
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14767
|
+
|
|
14768
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14769
|
+
symbolInfo: symbols,
|
|
14770
|
+
sourcePath: "test.cnx",
|
|
14771
|
+
});
|
|
14772
|
+
|
|
14773
|
+
// Bit range write should use mask-and-shift pattern
|
|
14774
|
+
expect(code).toContain("val");
|
|
14775
|
+
});
|
|
14776
|
+
|
|
14777
|
+
it("should generate single bit write", () => {
|
|
14778
|
+
const source = `
|
|
14779
|
+
void test() {
|
|
14780
|
+
u32 flags <- 0;
|
|
14781
|
+
flags[3] <- true;
|
|
14782
|
+
}
|
|
14783
|
+
`;
|
|
14784
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14785
|
+
const generator = new CodeGenerator();
|
|
14786
|
+
const symbolTable = new SymbolTable();
|
|
14787
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14788
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14789
|
+
|
|
14790
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14791
|
+
symbolInfo: symbols,
|
|
14792
|
+
sourcePath: "test.cnx",
|
|
14793
|
+
});
|
|
14794
|
+
|
|
14795
|
+
// Single bit write
|
|
14796
|
+
expect(code).toContain("flags");
|
|
14797
|
+
expect(code).toContain("1 << 3");
|
|
14798
|
+
});
|
|
14799
|
+
|
|
14800
|
+
it("should generate array element write", () => {
|
|
14801
|
+
const source = `
|
|
14802
|
+
void test() {
|
|
14803
|
+
u32[10] arr;
|
|
14804
|
+
arr[5] <- 42;
|
|
14805
|
+
}
|
|
14806
|
+
`;
|
|
14807
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14808
|
+
const generator = new CodeGenerator();
|
|
14809
|
+
const symbolTable = new SymbolTable();
|
|
14810
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14811
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14812
|
+
|
|
14813
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14814
|
+
symbolInfo: symbols,
|
|
14815
|
+
sourcePath: "test.cnx",
|
|
14816
|
+
});
|
|
14817
|
+
|
|
14818
|
+
expect(code).toContain("arr[5] = 42");
|
|
14819
|
+
});
|
|
14820
|
+
|
|
14821
|
+
it("should generate bit range at position 0", () => {
|
|
14822
|
+
const source = `
|
|
14823
|
+
void test() {
|
|
14824
|
+
u32 val <- 0x1234;
|
|
14825
|
+
u8 lowByte <- val[0, 8];
|
|
14826
|
+
}
|
|
14827
|
+
`;
|
|
14828
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14829
|
+
const generator = new CodeGenerator();
|
|
14830
|
+
const symbolTable = new SymbolTable();
|
|
14831
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14832
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14833
|
+
|
|
14834
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14835
|
+
symbolInfo: symbols,
|
|
14836
|
+
sourcePath: "test.cnx",
|
|
14837
|
+
});
|
|
14838
|
+
|
|
14839
|
+
// Position 0 should NOT generate >> 0
|
|
14840
|
+
expect(code).not.toContain(">> 0");
|
|
14841
|
+
});
|
|
14842
|
+
|
|
14843
|
+
it("should generate 64-bit mask for u64 values", () => {
|
|
14844
|
+
const source = `
|
|
14845
|
+
void test() {
|
|
14846
|
+
u64 val <- 0xFFFFFFFFFFFFFFFF;
|
|
14847
|
+
u32 upper <- val[32, 32];
|
|
14848
|
+
}
|
|
14849
|
+
`;
|
|
14850
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14851
|
+
const generator = new CodeGenerator();
|
|
14852
|
+
const symbolTable = new SymbolTable();
|
|
14853
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14854
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14855
|
+
|
|
14856
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14857
|
+
symbolInfo: symbols,
|
|
14858
|
+
sourcePath: "test.cnx",
|
|
14859
|
+
});
|
|
14860
|
+
|
|
14861
|
+
// 64-bit operations should have ULL suffix
|
|
14862
|
+
expect(code).toContain(">> 32");
|
|
14863
|
+
});
|
|
14864
|
+
});
|
|
14865
|
+
|
|
14866
|
+
describe("type name resolution", () => {
|
|
14867
|
+
it("should resolve this.Type for scoped types", () => {
|
|
14868
|
+
const source = `
|
|
14869
|
+
scope Motor {
|
|
14870
|
+
enum State { IDLE, RUNNING }
|
|
14871
|
+
public State getState() {
|
|
14872
|
+
return global.Motor.State.IDLE;
|
|
14873
|
+
}
|
|
14874
|
+
}
|
|
14875
|
+
`;
|
|
14876
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14877
|
+
const generator = new CodeGenerator();
|
|
14878
|
+
const symbolTable = new SymbolTable();
|
|
14879
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14880
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14881
|
+
|
|
14882
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14883
|
+
symbolInfo: symbols,
|
|
14884
|
+
sourcePath: "test.cnx",
|
|
14885
|
+
});
|
|
14886
|
+
|
|
14887
|
+
expect(code).toContain("Motor_State");
|
|
14888
|
+
});
|
|
14889
|
+
|
|
14890
|
+
it("should resolve global.Type for global types inside scope", () => {
|
|
14891
|
+
const source = `
|
|
14892
|
+
enum Color { RED, GREEN }
|
|
14893
|
+
scope Display {
|
|
14894
|
+
public void setColor(Color c) {
|
|
14895
|
+
Color myColor <- c;
|
|
14896
|
+
}
|
|
14897
|
+
}
|
|
14898
|
+
`;
|
|
14899
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14900
|
+
const generator = new CodeGenerator();
|
|
14901
|
+
const symbolTable = new SymbolTable();
|
|
14902
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14903
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14904
|
+
|
|
14905
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14906
|
+
symbolInfo: symbols,
|
|
14907
|
+
sourcePath: "test.cnx",
|
|
14908
|
+
});
|
|
14909
|
+
|
|
14910
|
+
expect(code).toContain("Color");
|
|
14911
|
+
});
|
|
14912
|
+
});
|
|
14913
|
+
|
|
14914
|
+
describe("switch statement generation", () => {
|
|
14915
|
+
it("should generate switch with exhaustive enum cases", () => {
|
|
14916
|
+
const source = `
|
|
14917
|
+
enum State { OFF, ON }
|
|
14918
|
+
void test() {
|
|
14919
|
+
State s <- State.OFF;
|
|
14920
|
+
switch (s) {
|
|
14921
|
+
case State.OFF { }
|
|
14922
|
+
case State.ON { }
|
|
14923
|
+
}
|
|
14924
|
+
}
|
|
14925
|
+
`;
|
|
14926
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14927
|
+
const generator = new CodeGenerator();
|
|
14928
|
+
const symbolTable = new SymbolTable();
|
|
14929
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14930
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14931
|
+
|
|
14932
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14933
|
+
symbolInfo: symbols,
|
|
14934
|
+
sourcePath: "test.cnx",
|
|
14935
|
+
});
|
|
14936
|
+
|
|
14937
|
+
expect(code).toContain("switch");
|
|
14938
|
+
expect(code).toContain("case State_OFF");
|
|
14939
|
+
expect(code).toContain("case State_ON");
|
|
14940
|
+
expect(code).toContain("break;");
|
|
14941
|
+
});
|
|
14942
|
+
|
|
14943
|
+
it("should generate switch with default case", () => {
|
|
14944
|
+
const source = `
|
|
14945
|
+
void test() {
|
|
14946
|
+
u32 val <- 5;
|
|
14947
|
+
switch (val) {
|
|
14948
|
+
case 1 { }
|
|
14949
|
+
case 2 { }
|
|
14950
|
+
default { }
|
|
14951
|
+
}
|
|
14952
|
+
}
|
|
14953
|
+
`;
|
|
14954
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14955
|
+
const generator = new CodeGenerator();
|
|
14956
|
+
const symbolTable = new SymbolTable();
|
|
14957
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14958
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14959
|
+
|
|
14960
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14961
|
+
symbolInfo: symbols,
|
|
14962
|
+
sourcePath: "test.cnx",
|
|
14963
|
+
});
|
|
14964
|
+
|
|
14965
|
+
expect(code).toContain("switch");
|
|
14966
|
+
expect(code).toContain("default:");
|
|
14967
|
+
});
|
|
14968
|
+
});
|
|
14969
|
+
|
|
14970
|
+
describe("while loop generation", () => {
|
|
14971
|
+
it("should generate while loop", () => {
|
|
14972
|
+
const source = `
|
|
14973
|
+
void test() {
|
|
14974
|
+
u32 i <- 0;
|
|
14975
|
+
while (i < 10) {
|
|
14976
|
+
i +<- 1;
|
|
14977
|
+
}
|
|
14978
|
+
}
|
|
14979
|
+
`;
|
|
14980
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
14981
|
+
const generator = new CodeGenerator();
|
|
14982
|
+
const symbolTable = new SymbolTable();
|
|
14983
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
14984
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
14985
|
+
|
|
14986
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
14987
|
+
symbolInfo: symbols,
|
|
14988
|
+
sourcePath: "test.cnx",
|
|
14989
|
+
});
|
|
14990
|
+
|
|
14991
|
+
expect(code).toContain("while (i < 10)");
|
|
14992
|
+
});
|
|
14993
|
+
});
|
|
14994
|
+
|
|
14995
|
+
describe("for loop generation", () => {
|
|
14996
|
+
it("should generate for loop", () => {
|
|
14997
|
+
const source = `
|
|
14998
|
+
void test() {
|
|
14999
|
+
for (u32 i <- 0; i < 10; i +<- 1) {
|
|
15000
|
+
u32 x <- i;
|
|
15001
|
+
}
|
|
15002
|
+
}
|
|
15003
|
+
`;
|
|
15004
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15005
|
+
const generator = new CodeGenerator();
|
|
15006
|
+
const symbolTable = new SymbolTable();
|
|
15007
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15008
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15009
|
+
|
|
15010
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15011
|
+
symbolInfo: symbols,
|
|
15012
|
+
sourcePath: "test.cnx",
|
|
15013
|
+
});
|
|
15014
|
+
|
|
15015
|
+
expect(code).toContain("for (");
|
|
15016
|
+
expect(code).toContain("uint32_t i = 0");
|
|
15017
|
+
expect(code).toContain("i < 10");
|
|
15018
|
+
});
|
|
15019
|
+
});
|
|
15020
|
+
|
|
15021
|
+
describe("do-while loop generation", () => {
|
|
15022
|
+
it("should generate do-while loop", () => {
|
|
15023
|
+
const source = `
|
|
15024
|
+
void test() {
|
|
15025
|
+
u32 i <- 0;
|
|
15026
|
+
do {
|
|
15027
|
+
i +<- 1;
|
|
15028
|
+
} while (i < 10);
|
|
15029
|
+
}
|
|
15030
|
+
`;
|
|
15031
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15032
|
+
const generator = new CodeGenerator();
|
|
15033
|
+
const symbolTable = new SymbolTable();
|
|
15034
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15035
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15036
|
+
|
|
15037
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15038
|
+
symbolInfo: symbols,
|
|
15039
|
+
sourcePath: "test.cnx",
|
|
15040
|
+
});
|
|
15041
|
+
|
|
15042
|
+
expect(code).toContain("do {");
|
|
15043
|
+
expect(code).toContain("} while (i < 10)");
|
|
15044
|
+
});
|
|
15045
|
+
});
|
|
15046
|
+
|
|
15047
|
+
describe("break and continue statements", () => {
|
|
15048
|
+
it("should generate break in loop", () => {
|
|
15049
|
+
const source = `
|
|
15050
|
+
void test() {
|
|
15051
|
+
while (true) {
|
|
15052
|
+
break;
|
|
15053
|
+
}
|
|
15054
|
+
}
|
|
15055
|
+
`;
|
|
15056
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15057
|
+
const generator = new CodeGenerator();
|
|
15058
|
+
const symbolTable = new SymbolTable();
|
|
15059
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15060
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15061
|
+
|
|
15062
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15063
|
+
symbolInfo: symbols,
|
|
15064
|
+
sourcePath: "test.cnx",
|
|
15065
|
+
});
|
|
15066
|
+
|
|
15067
|
+
expect(code).toContain("break;");
|
|
15068
|
+
});
|
|
15069
|
+
|
|
15070
|
+
it("should generate continue in loop", () => {
|
|
15071
|
+
const source = `
|
|
15072
|
+
void test() {
|
|
15073
|
+
u32 i <- 0;
|
|
15074
|
+
while (i < 10) {
|
|
15075
|
+
i +<- 1;
|
|
15076
|
+
if (i = 5) {
|
|
15077
|
+
continue;
|
|
15078
|
+
}
|
|
15079
|
+
}
|
|
15080
|
+
}
|
|
15081
|
+
`;
|
|
15082
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15083
|
+
const generator = new CodeGenerator();
|
|
15084
|
+
const symbolTable = new SymbolTable();
|
|
15085
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15086
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15087
|
+
|
|
15088
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15089
|
+
symbolInfo: symbols,
|
|
15090
|
+
sourcePath: "test.cnx",
|
|
15091
|
+
});
|
|
15092
|
+
|
|
15093
|
+
expect(code).toContain("continue;");
|
|
15094
|
+
});
|
|
15095
|
+
});
|
|
15096
|
+
|
|
15097
|
+
describe("function with parameters", () => {
|
|
15098
|
+
it("should generate function with multiple params", () => {
|
|
15099
|
+
const source = `
|
|
15100
|
+
u32 add(u32 a, u32 b) {
|
|
15101
|
+
return a + b;
|
|
15102
|
+
}
|
|
15103
|
+
`;
|
|
15104
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15105
|
+
const generator = new CodeGenerator();
|
|
15106
|
+
const symbolTable = new SymbolTable();
|
|
15107
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15108
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15109
|
+
|
|
15110
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15111
|
+
symbolInfo: symbols,
|
|
15112
|
+
sourcePath: "test.cnx",
|
|
15113
|
+
});
|
|
15114
|
+
|
|
15115
|
+
expect(code).toContain("uint32_t add(uint32_t a, uint32_t b)");
|
|
15116
|
+
expect(code).toContain("return a + b");
|
|
15117
|
+
});
|
|
15118
|
+
|
|
15119
|
+
it("should generate function with array parameter", () => {
|
|
15120
|
+
const source = `
|
|
15121
|
+
void process(u32 arr[10]) {
|
|
15122
|
+
u32 val <- arr[0];
|
|
15123
|
+
}
|
|
15124
|
+
`;
|
|
15125
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15126
|
+
const generator = new CodeGenerator();
|
|
15127
|
+
const symbolTable = new SymbolTable();
|
|
15128
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15129
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15130
|
+
|
|
15131
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15132
|
+
symbolInfo: symbols,
|
|
15133
|
+
sourcePath: "test.cnx",
|
|
15134
|
+
});
|
|
15135
|
+
|
|
15136
|
+
expect(code).toContain("uint32_t arr[10]");
|
|
15137
|
+
});
|
|
15138
|
+
});
|
|
15139
|
+
|
|
15140
|
+
describe("global variable declarations", () => {
|
|
15141
|
+
it("should generate global variable initialization", () => {
|
|
15142
|
+
const source = `
|
|
15143
|
+
u32 globalCounter <- 0;
|
|
15144
|
+
`;
|
|
15145
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15146
|
+
const generator = new CodeGenerator();
|
|
15147
|
+
const symbolTable = new SymbolTable();
|
|
15148
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15149
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15150
|
+
|
|
15151
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15152
|
+
symbolInfo: symbols,
|
|
15153
|
+
sourcePath: "test.cnx",
|
|
15154
|
+
});
|
|
15155
|
+
|
|
15156
|
+
expect(code).toContain("uint32_t globalCounter");
|
|
15157
|
+
expect(code).toContain("= 0");
|
|
15158
|
+
});
|
|
15159
|
+
|
|
15160
|
+
it("should generate const global", () => {
|
|
15161
|
+
const source = `
|
|
15162
|
+
const u32 MAX_VALUE <- 100;
|
|
15163
|
+
`;
|
|
15164
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15165
|
+
const generator = new CodeGenerator();
|
|
15166
|
+
const symbolTable = new SymbolTable();
|
|
15167
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15168
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15169
|
+
|
|
15170
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15171
|
+
symbolInfo: symbols,
|
|
15172
|
+
sourcePath: "test.cnx",
|
|
15173
|
+
});
|
|
15174
|
+
|
|
15175
|
+
expect(code).toContain("const uint32_t MAX_VALUE");
|
|
15176
|
+
expect(code).toContain("= 100");
|
|
15177
|
+
});
|
|
15178
|
+
});
|
|
15179
|
+
|
|
15180
|
+
describe("arithmetic expressions", () => {
|
|
15181
|
+
it("should generate modulo operation", () => {
|
|
15182
|
+
const source = `
|
|
15183
|
+
void test() {
|
|
15184
|
+
u32 a <- 10;
|
|
15185
|
+
u32 b <- a % 3;
|
|
15186
|
+
}
|
|
15187
|
+
`;
|
|
15188
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15189
|
+
const generator = new CodeGenerator();
|
|
15190
|
+
const symbolTable = new SymbolTable();
|
|
15191
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15192
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15193
|
+
|
|
15194
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15195
|
+
symbolInfo: symbols,
|
|
15196
|
+
sourcePath: "test.cnx",
|
|
15197
|
+
});
|
|
15198
|
+
|
|
15199
|
+
expect(code).toContain("a % 3");
|
|
15200
|
+
});
|
|
15201
|
+
|
|
15202
|
+
it("should generate bitwise operations", () => {
|
|
15203
|
+
const source = `
|
|
15204
|
+
void test() {
|
|
15205
|
+
u32 a <- 0xFF;
|
|
15206
|
+
u32 b <- a & 0x0F;
|
|
15207
|
+
u32 c <- a | 0xF0;
|
|
15208
|
+
u32 d <- a ^ 0xAA;
|
|
15209
|
+
u32 e <- ~a;
|
|
15210
|
+
}
|
|
15211
|
+
`;
|
|
15212
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15213
|
+
const generator = new CodeGenerator();
|
|
15214
|
+
const symbolTable = new SymbolTable();
|
|
15215
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15216
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15217
|
+
|
|
15218
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15219
|
+
symbolInfo: symbols,
|
|
15220
|
+
sourcePath: "test.cnx",
|
|
15221
|
+
});
|
|
15222
|
+
|
|
15223
|
+
expect(code).toContain("a & 0x0F");
|
|
15224
|
+
expect(code).toContain("a | 0xF0");
|
|
15225
|
+
expect(code).toContain("a ^ 0xAA");
|
|
15226
|
+
expect(code).toContain("~a");
|
|
15227
|
+
});
|
|
15228
|
+
|
|
15229
|
+
it("should generate shift operations", () => {
|
|
15230
|
+
const source = `
|
|
15231
|
+
void test() {
|
|
15232
|
+
u32 a <- 1;
|
|
15233
|
+
u32 b <- a << 4;
|
|
15234
|
+
u32 c <- a >> 2;
|
|
15235
|
+
}
|
|
15236
|
+
`;
|
|
15237
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15238
|
+
const generator = new CodeGenerator();
|
|
15239
|
+
const symbolTable = new SymbolTable();
|
|
15240
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15241
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15242
|
+
|
|
15243
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15244
|
+
symbolInfo: symbols,
|
|
15245
|
+
sourcePath: "test.cnx",
|
|
15246
|
+
});
|
|
15247
|
+
|
|
15248
|
+
expect(code).toContain("a << 4");
|
|
15249
|
+
expect(code).toContain("a >> 2");
|
|
15250
|
+
});
|
|
15251
|
+
});
|
|
15252
|
+
|
|
15253
|
+
describe("logical expressions", () => {
|
|
15254
|
+
it("should generate logical AND and OR", () => {
|
|
15255
|
+
const source = `
|
|
15256
|
+
void test() {
|
|
15257
|
+
bool a <- true;
|
|
15258
|
+
bool b <- false;
|
|
15259
|
+
bool c <- a && b;
|
|
15260
|
+
bool d <- a || b;
|
|
15261
|
+
}
|
|
15262
|
+
`;
|
|
15263
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15264
|
+
const generator = new CodeGenerator();
|
|
15265
|
+
const symbolTable = new SymbolTable();
|
|
15266
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15267
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15268
|
+
|
|
15269
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15270
|
+
symbolInfo: symbols,
|
|
15271
|
+
sourcePath: "test.cnx",
|
|
15272
|
+
});
|
|
15273
|
+
|
|
15274
|
+
expect(code).toContain("a && b");
|
|
15275
|
+
expect(code).toContain("a || b");
|
|
15276
|
+
});
|
|
15277
|
+
|
|
15278
|
+
it("should generate NOT operation", () => {
|
|
15279
|
+
const source = `
|
|
15280
|
+
void test() {
|
|
15281
|
+
bool a <- true;
|
|
15282
|
+
bool b <- !a;
|
|
15283
|
+
}
|
|
15284
|
+
`;
|
|
15285
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15286
|
+
const generator = new CodeGenerator();
|
|
15287
|
+
const symbolTable = new SymbolTable();
|
|
15288
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15289
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15290
|
+
|
|
15291
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15292
|
+
symbolInfo: symbols,
|
|
15293
|
+
sourcePath: "test.cnx",
|
|
15294
|
+
});
|
|
15295
|
+
|
|
15296
|
+
expect(code).toContain("!a");
|
|
15297
|
+
});
|
|
15298
|
+
});
|
|
15299
|
+
|
|
15300
|
+
describe("ternary expression", () => {
|
|
15301
|
+
it("should generate ternary expression", () => {
|
|
15302
|
+
const source = `
|
|
15303
|
+
void test() {
|
|
15304
|
+
u32 a <- 5;
|
|
15305
|
+
u32 b <- (a > 3) ? 10 : 20;
|
|
15306
|
+
}
|
|
15307
|
+
`;
|
|
15308
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15309
|
+
const generator = new CodeGenerator();
|
|
15310
|
+
const symbolTable = new SymbolTable();
|
|
15311
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15312
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15313
|
+
|
|
15314
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15315
|
+
symbolInfo: symbols,
|
|
15316
|
+
sourcePath: "test.cnx",
|
|
15317
|
+
});
|
|
15318
|
+
|
|
15319
|
+
expect(code).toContain("a > 3");
|
|
15320
|
+
expect(code).toContain("?");
|
|
15321
|
+
expect(code).toContain("10");
|
|
15322
|
+
expect(code).toContain("20");
|
|
15323
|
+
});
|
|
15324
|
+
});
|
|
15325
|
+
|
|
15326
|
+
describe("unary expressions", () => {
|
|
15327
|
+
it("should generate negative number", () => {
|
|
15328
|
+
const source = `
|
|
15329
|
+
void test() {
|
|
15330
|
+
i32 a <- 5;
|
|
15331
|
+
i32 b <- -a;
|
|
15332
|
+
}
|
|
15333
|
+
`;
|
|
15334
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15335
|
+
const generator = new CodeGenerator();
|
|
15336
|
+
const symbolTable = new SymbolTable();
|
|
15337
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15338
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15339
|
+
|
|
15340
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15341
|
+
symbolInfo: symbols,
|
|
15342
|
+
sourcePath: "test.cnx",
|
|
15343
|
+
});
|
|
15344
|
+
|
|
15345
|
+
expect(code).toContain("-a");
|
|
15346
|
+
});
|
|
15347
|
+
|
|
15348
|
+
it("should generate bitwise NOT", () => {
|
|
15349
|
+
const source = `
|
|
15350
|
+
void test() {
|
|
15351
|
+
u32 a <- 0xFF;
|
|
15352
|
+
u32 b <- ~a;
|
|
15353
|
+
}
|
|
15354
|
+
`;
|
|
15355
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15356
|
+
const generator = new CodeGenerator();
|
|
15357
|
+
const symbolTable = new SymbolTable();
|
|
15358
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15359
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15360
|
+
|
|
15361
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15362
|
+
symbolInfo: symbols,
|
|
15363
|
+
sourcePath: "test.cnx",
|
|
15364
|
+
});
|
|
15365
|
+
|
|
15366
|
+
expect(code).toContain("~a");
|
|
15367
|
+
});
|
|
15368
|
+
});
|
|
15369
|
+
|
|
15370
|
+
describe("bitmap type", () => {
|
|
15371
|
+
it("should generate bitmap field access", () => {
|
|
15372
|
+
const source = `
|
|
15373
|
+
bitmap8 Flags {
|
|
15374
|
+
bit0,
|
|
15375
|
+
bit1,
|
|
15376
|
+
bit2,
|
|
15377
|
+
bit3,
|
|
15378
|
+
bit4,
|
|
15379
|
+
bit5,
|
|
15380
|
+
bit6,
|
|
15381
|
+
bit7
|
|
15382
|
+
}
|
|
15383
|
+
void test() {
|
|
15384
|
+
Flags f <- 0;
|
|
15385
|
+
bool isSet <- f.bit0;
|
|
15386
|
+
}
|
|
15387
|
+
`;
|
|
15388
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15389
|
+
const generator = new CodeGenerator();
|
|
15390
|
+
const symbolTable = new SymbolTable();
|
|
15391
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15392
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15393
|
+
|
|
15394
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15395
|
+
symbolInfo: symbols,
|
|
15396
|
+
sourcePath: "test.cnx",
|
|
15397
|
+
});
|
|
15398
|
+
|
|
15399
|
+
expect(code).toContain("Flags");
|
|
15400
|
+
});
|
|
15401
|
+
});
|
|
15402
|
+
|
|
15403
|
+
describe("function call expressions", () => {
|
|
15404
|
+
it("should generate function call with arguments", () => {
|
|
15405
|
+
const source = `
|
|
15406
|
+
u32 multiply(u32 x, u32 y) {
|
|
15407
|
+
return x * y;
|
|
15408
|
+
}
|
|
15409
|
+
void test() {
|
|
15410
|
+
u32 result <- multiply(5, 10);
|
|
15411
|
+
}
|
|
15412
|
+
`;
|
|
15413
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15414
|
+
const generator = new CodeGenerator();
|
|
15415
|
+
const symbolTable = new SymbolTable();
|
|
15416
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15417
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15418
|
+
|
|
15419
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15420
|
+
symbolInfo: symbols,
|
|
15421
|
+
sourcePath: "test.cnx",
|
|
15422
|
+
});
|
|
15423
|
+
|
|
15424
|
+
expect(code).toContain("multiply(5, 10)");
|
|
15425
|
+
});
|
|
15426
|
+
|
|
15427
|
+
it("should generate scoped function call", () => {
|
|
15428
|
+
const source = `
|
|
15429
|
+
scope Math {
|
|
15430
|
+
public u32 square(u32 x) {
|
|
15431
|
+
return x * x;
|
|
15432
|
+
}
|
|
15433
|
+
}
|
|
15434
|
+
void test() {
|
|
15435
|
+
u32 result <- Math.square(5);
|
|
15436
|
+
}
|
|
15437
|
+
`;
|
|
15438
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15439
|
+
const generator = new CodeGenerator();
|
|
15440
|
+
const symbolTable = new SymbolTable();
|
|
15441
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15442
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15443
|
+
|
|
15444
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15445
|
+
symbolInfo: symbols,
|
|
15446
|
+
sourcePath: "test.cnx",
|
|
15447
|
+
});
|
|
15448
|
+
|
|
15449
|
+
expect(code).toContain("Math_square(5)");
|
|
15450
|
+
});
|
|
15451
|
+
});
|
|
15452
|
+
|
|
15453
|
+
describe("ISR declaration", () => {
|
|
15454
|
+
it("should generate ISR attribute", () => {
|
|
15455
|
+
const source = `
|
|
15456
|
+
isr void TIMER0_IRQ() {
|
|
15457
|
+
u32 x <- 1;
|
|
15458
|
+
}
|
|
15459
|
+
`;
|
|
15460
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15461
|
+
const generator = new CodeGenerator();
|
|
15462
|
+
const symbolTable = new SymbolTable();
|
|
15463
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15464
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15465
|
+
|
|
15466
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15467
|
+
symbolInfo: symbols,
|
|
15468
|
+
sourcePath: "test.cnx",
|
|
15469
|
+
});
|
|
15470
|
+
|
|
15471
|
+
// ISR functions should have special attributes
|
|
15472
|
+
expect(code).toContain("TIMER0_IRQ");
|
|
15473
|
+
});
|
|
15474
|
+
});
|
|
15475
|
+
|
|
15476
|
+
describe("atomic variable", () => {
|
|
15477
|
+
it("should generate volatile for atomic", () => {
|
|
15478
|
+
const source = `
|
|
15479
|
+
atomic u32 counter <- 0;
|
|
15480
|
+
void test() {
|
|
15481
|
+
counter <- 1;
|
|
15482
|
+
}
|
|
15483
|
+
`;
|
|
15484
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15485
|
+
const generator = new CodeGenerator();
|
|
15486
|
+
const symbolTable = new SymbolTable();
|
|
15487
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15488
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15489
|
+
|
|
15490
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15491
|
+
symbolInfo: symbols,
|
|
15492
|
+
sourcePath: "test.cnx",
|
|
15493
|
+
});
|
|
15494
|
+
|
|
15495
|
+
expect(code).toContain("volatile");
|
|
15496
|
+
});
|
|
15497
|
+
});
|
|
15498
|
+
|
|
15499
|
+
describe("wrap overflow behavior", () => {
|
|
15500
|
+
it("should not add helpers for wrap types", () => {
|
|
15501
|
+
const source = `
|
|
15502
|
+
wrap u8 val <- 250;
|
|
15503
|
+
void test() {
|
|
15504
|
+
val +<- 10;
|
|
15505
|
+
}
|
|
15506
|
+
`;
|
|
15507
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15508
|
+
const generator = new CodeGenerator();
|
|
15509
|
+
const symbolTable = new SymbolTable();
|
|
15510
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15511
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15512
|
+
|
|
15513
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15514
|
+
symbolInfo: symbols,
|
|
15515
|
+
sourcePath: "test.cnx",
|
|
15516
|
+
});
|
|
15517
|
+
|
|
15518
|
+
// Wrap types just use += without helper
|
|
15519
|
+
expect(code).toContain("+=");
|
|
15520
|
+
expect(code).not.toContain("cnx_");
|
|
15521
|
+
});
|
|
15522
|
+
});
|
|
15523
|
+
|
|
15524
|
+
describe("safe division", () => {
|
|
15525
|
+
it("should generate safe division helper", () => {
|
|
15526
|
+
const source = `
|
|
15527
|
+
void test() {
|
|
15528
|
+
u32 a <- 10;
|
|
15529
|
+
u32 b <- 0;
|
|
15530
|
+
u32 c <- a / b;
|
|
15531
|
+
}
|
|
15532
|
+
`;
|
|
15533
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15534
|
+
const generator = new CodeGenerator();
|
|
15535
|
+
const symbolTable = new SymbolTable();
|
|
15536
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15537
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15538
|
+
|
|
15539
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15540
|
+
symbolInfo: symbols,
|
|
15541
|
+
sourcePath: "test.cnx",
|
|
15542
|
+
});
|
|
15543
|
+
|
|
15544
|
+
// Division should work
|
|
15545
|
+
expect(code).toContain("a / b");
|
|
15546
|
+
});
|
|
15547
|
+
});
|
|
15548
|
+
|
|
15549
|
+
describe("parenthesized expressions", () => {
|
|
15550
|
+
it("should preserve parentheses in expressions", () => {
|
|
15551
|
+
const source = `
|
|
15552
|
+
void test() {
|
|
15553
|
+
u32 a <- 2;
|
|
15554
|
+
u32 b <- 3;
|
|
15555
|
+
u32 c <- 4;
|
|
15556
|
+
u32 result <- (a + b) * c;
|
|
15557
|
+
}
|
|
15558
|
+
`;
|
|
15559
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15560
|
+
const generator = new CodeGenerator();
|
|
15561
|
+
const symbolTable = new SymbolTable();
|
|
15562
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15563
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15564
|
+
|
|
15565
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15566
|
+
symbolInfo: symbols,
|
|
15567
|
+
sourcePath: "test.cnx",
|
|
15568
|
+
});
|
|
15569
|
+
|
|
15570
|
+
expect(code).toContain("(a + b) * c");
|
|
15571
|
+
});
|
|
15572
|
+
});
|
|
15573
|
+
|
|
15574
|
+
describe("array access via ArrayAccessHelper", () => {
|
|
15575
|
+
it("should generate single-index array access", () => {
|
|
15576
|
+
const source = `
|
|
15577
|
+
u32[10] arr;
|
|
15578
|
+
void test() {
|
|
15579
|
+
u32 val <- arr[5];
|
|
15580
|
+
}
|
|
15581
|
+
`;
|
|
15582
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15583
|
+
const generator = new CodeGenerator();
|
|
15584
|
+
const symbolTable = new SymbolTable();
|
|
15585
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15586
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15587
|
+
|
|
15588
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15589
|
+
symbolInfo: symbols,
|
|
15590
|
+
sourcePath: "test.cnx",
|
|
15591
|
+
});
|
|
15592
|
+
|
|
15593
|
+
expect(code).toContain("arr[5]");
|
|
15594
|
+
});
|
|
15595
|
+
|
|
15596
|
+
it("should generate array access with variable index", () => {
|
|
15597
|
+
const source = `
|
|
15598
|
+
u8[100] data;
|
|
15599
|
+
void test(u32 idx) {
|
|
15600
|
+
u8 val <- data[idx];
|
|
15601
|
+
}
|
|
15602
|
+
`;
|
|
15603
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15604
|
+
const generator = new CodeGenerator();
|
|
15605
|
+
const symbolTable = new SymbolTable();
|
|
15606
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15607
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15608
|
+
|
|
15609
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15610
|
+
symbolInfo: symbols,
|
|
15611
|
+
sourcePath: "test.cnx",
|
|
15612
|
+
});
|
|
15613
|
+
|
|
15614
|
+
expect(code).toContain("data[idx]");
|
|
15615
|
+
});
|
|
15616
|
+
|
|
15617
|
+
it("should generate integer bit range access", () => {
|
|
15618
|
+
const source = `
|
|
15619
|
+
void test() {
|
|
15620
|
+
u32 value <- 0xFF00;
|
|
15621
|
+
u8 byte <- value[8, 8];
|
|
15622
|
+
}
|
|
15623
|
+
`;
|
|
15624
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15625
|
+
const generator = new CodeGenerator();
|
|
15626
|
+
const symbolTable = new SymbolTable();
|
|
15627
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15628
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15629
|
+
|
|
15630
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15631
|
+
symbolInfo: symbols,
|
|
15632
|
+
sourcePath: "test.cnx",
|
|
15633
|
+
});
|
|
15634
|
+
|
|
15635
|
+
expect(code).toContain(">> 8");
|
|
15636
|
+
expect(code).toContain("& 0xFF");
|
|
15637
|
+
});
|
|
15638
|
+
|
|
15639
|
+
it("should generate float bit range access with memcpy", () => {
|
|
15640
|
+
const source = `
|
|
15641
|
+
void test() {
|
|
15642
|
+
f32 fval <- 1.5;
|
|
15643
|
+
u8 byte <- fval[0, 8];
|
|
15644
|
+
}
|
|
15645
|
+
`;
|
|
15646
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15647
|
+
const generator = new CodeGenerator();
|
|
15648
|
+
const symbolTable = new SymbolTable();
|
|
15649
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15650
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15651
|
+
|
|
15652
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15653
|
+
symbolInfo: symbols,
|
|
15654
|
+
sourcePath: "test.cnx",
|
|
15655
|
+
});
|
|
15656
|
+
|
|
15657
|
+
expect(code).toContain("memcpy");
|
|
15658
|
+
expect(code).toContain("__bits_fval");
|
|
15659
|
+
});
|
|
15660
|
+
|
|
15661
|
+
it("should generate f64 bit range access with uint64_t shadow", () => {
|
|
15662
|
+
const source = `
|
|
15663
|
+
void test() {
|
|
15664
|
+
f64 dval <- 1.5;
|
|
15665
|
+
u8 byte <- dval[0, 8];
|
|
15666
|
+
}
|
|
15667
|
+
`;
|
|
15668
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15669
|
+
const generator = new CodeGenerator();
|
|
15670
|
+
const symbolTable = new SymbolTable();
|
|
15671
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15672
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15673
|
+
|
|
15674
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15675
|
+
symbolInfo: symbols,
|
|
15676
|
+
sourcePath: "test.cnx",
|
|
15677
|
+
});
|
|
15678
|
+
|
|
15679
|
+
expect(code).toContain("uint64_t __bits_dval");
|
|
15680
|
+
expect(code).toContain("memcpy");
|
|
15681
|
+
});
|
|
15682
|
+
});
|
|
15683
|
+
});
|
|
15684
|
+
|
|
15685
|
+
describe("Issue #741: const scope members bare reference inlining", () => {
|
|
15686
|
+
it("should inline private const when referenced without this. prefix", () => {
|
|
15687
|
+
const source = `
|
|
15688
|
+
scope Foo {
|
|
15689
|
+
const u32 MY_CONSTANT <- 42;
|
|
15690
|
+
|
|
15691
|
+
public u32 getConstant() {
|
|
15692
|
+
return MY_CONSTANT;
|
|
15693
|
+
}
|
|
15694
|
+
}
|
|
15695
|
+
`;
|
|
15696
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15697
|
+
const generator = new CodeGenerator();
|
|
15698
|
+
const symbolTable = new SymbolTable();
|
|
15699
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15700
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15701
|
+
|
|
15702
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15703
|
+
symbolInfo: symbols,
|
|
15704
|
+
sourcePath: "test.cnx",
|
|
15705
|
+
});
|
|
15706
|
+
|
|
15707
|
+
// Should inline the value, not generate a reference to Foo_MY_CONSTANT
|
|
15708
|
+
expect(code).toContain("return 42;");
|
|
15709
|
+
expect(code).not.toContain("Foo_MY_CONSTANT");
|
|
15710
|
+
});
|
|
15711
|
+
|
|
15712
|
+
it("should inline private const in expressions without this. prefix", () => {
|
|
15713
|
+
const source = `
|
|
15714
|
+
scope Bar {
|
|
15715
|
+
const u8 OFFSET <- 10;
|
|
15716
|
+
|
|
15717
|
+
public u8 compute() {
|
|
15718
|
+
u8 result <- OFFSET + 5;
|
|
15719
|
+
return result;
|
|
15720
|
+
}
|
|
15721
|
+
}
|
|
15722
|
+
`;
|
|
15723
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15724
|
+
const generator = new CodeGenerator();
|
|
15725
|
+
const symbolTable = new SymbolTable();
|
|
15726
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15727
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15728
|
+
|
|
15729
|
+
const code = generator.generate(tree, symbolTable, tokenStream, {
|
|
15730
|
+
symbolInfo: symbols,
|
|
15731
|
+
sourcePath: "test.cnx",
|
|
15732
|
+
});
|
|
15733
|
+
|
|
15734
|
+
// Expression should be constant-folded: 10 + 5 = 15
|
|
15735
|
+
expect(code).toContain("uint8_t result = 15;");
|
|
15736
|
+
expect(code).not.toContain("Bar_OFFSET");
|
|
15737
|
+
});
|
|
15738
|
+
});
|
|
15739
|
+
|
|
15740
|
+
describe("Generator registration error paths", () => {
|
|
15741
|
+
it("throws error when struct generator is not registered", () => {
|
|
15742
|
+
// First call initializes the registry
|
|
15743
|
+
const initSource = `u8 x;`;
|
|
15744
|
+
const { tree: initTree, tokenStream: initTokenStream } =
|
|
15745
|
+
CNextSourceParser.parse(initSource);
|
|
15746
|
+
const generator = new CodeGenerator();
|
|
15747
|
+
const initSymbolTable = new SymbolTable();
|
|
15748
|
+
const initTSymbols = CNextResolver.resolve(initTree, "init.cnx");
|
|
15749
|
+
const initSymbols = TSymbolInfoAdapter.convert(initTSymbols);
|
|
15750
|
+
generator.generate(initTree, initSymbolTable, initTokenStream, {
|
|
15751
|
+
symbolInfo: initSymbols,
|
|
15752
|
+
sourcePath: "init.cnx",
|
|
15753
|
+
});
|
|
15754
|
+
|
|
15755
|
+
// Unregister the struct generator to test error path
|
|
15756
|
+
const registry = (
|
|
15757
|
+
generator as unknown as {
|
|
15758
|
+
registry: { unregisterDeclaration: (kind: string) => void };
|
|
15759
|
+
}
|
|
15760
|
+
).registry;
|
|
15761
|
+
registry.unregisterDeclaration("struct");
|
|
15762
|
+
|
|
15763
|
+
// Second call should throw because struct generator is now missing
|
|
15764
|
+
const source = `struct Point { i32 x; i32 y; }`;
|
|
15765
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15766
|
+
const symbolTable = new SymbolTable();
|
|
15767
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15768
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15769
|
+
|
|
15770
|
+
expect(() => {
|
|
15771
|
+
generator.generate(tree, symbolTable, tokenStream, {
|
|
15772
|
+
symbolInfo: symbols,
|
|
15773
|
+
sourcePath: "test.cnx",
|
|
15774
|
+
});
|
|
15775
|
+
}).toThrow("Error: struct generator not registered");
|
|
15776
|
+
});
|
|
15777
|
+
|
|
15778
|
+
it("throws error when enum generator is not registered", () => {
|
|
15779
|
+
// First call initializes the registry
|
|
15780
|
+
const initSource = `u8 x;`;
|
|
15781
|
+
const { tree: initTree, tokenStream: initTokenStream } =
|
|
15782
|
+
CNextSourceParser.parse(initSource);
|
|
15783
|
+
const generator = new CodeGenerator();
|
|
15784
|
+
const initSymbolTable = new SymbolTable();
|
|
15785
|
+
const initTSymbols = CNextResolver.resolve(initTree, "init.cnx");
|
|
15786
|
+
const initSymbols = TSymbolInfoAdapter.convert(initTSymbols);
|
|
15787
|
+
generator.generate(initTree, initSymbolTable, initTokenStream, {
|
|
15788
|
+
symbolInfo: initSymbols,
|
|
15789
|
+
sourcePath: "init.cnx",
|
|
15790
|
+
});
|
|
15791
|
+
|
|
15792
|
+
// Unregister the enum generator to test error path
|
|
15793
|
+
const registry = (
|
|
15794
|
+
generator as unknown as {
|
|
15795
|
+
registry: { unregisterDeclaration: (kind: string) => void };
|
|
15796
|
+
}
|
|
15797
|
+
).registry;
|
|
15798
|
+
registry.unregisterDeclaration("enum");
|
|
15799
|
+
|
|
15800
|
+
// Second call should throw because enum generator is now missing
|
|
15801
|
+
const source = `enum State { IDLE, RUNNING }`;
|
|
15802
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
15803
|
+
const symbolTable = new SymbolTable();
|
|
15804
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
15805
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
15806
|
+
|
|
15807
|
+
expect(() => {
|
|
15808
|
+
generator.generate(tree, symbolTable, tokenStream, {
|
|
15809
|
+
symbolInfo: symbols,
|
|
15810
|
+
sourcePath: "test.cnx",
|
|
15811
|
+
});
|
|
15812
|
+
}).toThrow("Error: enum generator not registered");
|
|
13783
15813
|
});
|
|
13784
15814
|
});
|
|
13785
15815
|
});
|