c-next 0.2.2 → 0.2.4
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 +59 -57
- package/dist/index.js +641 -191
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/cli/Runner.ts +1 -1
- package/src/cli/__tests__/Runner.test.ts +8 -8
- package/src/cli/serve/ServeCommand.ts +29 -9
- package/src/transpiler/Transpiler.ts +105 -200
- package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
- package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
- package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
- package/src/transpiler/data/IncludeResolver.ts +11 -3
- package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
- package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +170 -14
- package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +327 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
- package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
- package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
- package/src/transpiler/logic/symbols/c/index.ts +50 -1
- package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +99 -2
- package/src/transpiler/logic/symbols/c/utils/__tests__/DeclaratorUtils.test.ts +128 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +31 -5
- package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +49 -36
- package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +90 -25
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +23 -14
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +10 -7
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
- package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +10 -1
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +6 -3
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +36 -22
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +9 -1
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +8 -6
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +34 -18
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
- package/src/transpiler/state/CodeGenState.ts +6 -0
- package/src/transpiler/types/IPipelineFile.ts +2 -2
- package/src/transpiler/types/IPipelineInput.ts +1 -1
- package/src/transpiler/types/TTranspileInput.ts +18 -0
- package/src/utils/constants/TypeConstants.ts +22 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for DeclaratorUtils
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
6
|
+
import DeclaratorUtils from "../DeclaratorUtils";
|
|
7
|
+
import HeaderParser from "../../../../parser/HeaderParser";
|
|
8
|
+
|
|
9
|
+
describe("DeclaratorUtils", () => {
|
|
10
|
+
describe("extractTypeFromSpecQualList", () => {
|
|
11
|
+
it("should extract simple type", () => {
|
|
12
|
+
// Parse a struct with a simple int field
|
|
13
|
+
const source = `struct Test { int value; };`;
|
|
14
|
+
const { tree } = HeaderParser.parseC(source);
|
|
15
|
+
|
|
16
|
+
// Navigate to the struct's field's specifierQualifierList
|
|
17
|
+
const structSpec = tree
|
|
18
|
+
?.translationUnit()
|
|
19
|
+
?.externalDeclaration(0)
|
|
20
|
+
?.declaration()
|
|
21
|
+
?.declarationSpecifiers()
|
|
22
|
+
?.declarationSpecifier(0)
|
|
23
|
+
?.typeSpecifier()
|
|
24
|
+
?.structOrUnionSpecifier();
|
|
25
|
+
|
|
26
|
+
const fieldDecl = structSpec
|
|
27
|
+
?.structDeclarationList()
|
|
28
|
+
?.structDeclaration(0);
|
|
29
|
+
const specQualList = fieldDecl?.specifierQualifierList();
|
|
30
|
+
|
|
31
|
+
const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
|
|
32
|
+
expect(result).toBe("int");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should extract unsigned int type", () => {
|
|
36
|
+
const source = `struct Test { unsigned int count; };`;
|
|
37
|
+
const { tree } = HeaderParser.parseC(source);
|
|
38
|
+
|
|
39
|
+
const structSpec = tree
|
|
40
|
+
?.translationUnit()
|
|
41
|
+
?.externalDeclaration(0)
|
|
42
|
+
?.declaration()
|
|
43
|
+
?.declarationSpecifiers()
|
|
44
|
+
?.declarationSpecifier(0)
|
|
45
|
+
?.typeSpecifier()
|
|
46
|
+
?.structOrUnionSpecifier();
|
|
47
|
+
|
|
48
|
+
const fieldDecl = structSpec
|
|
49
|
+
?.structDeclarationList()
|
|
50
|
+
?.structDeclaration(0);
|
|
51
|
+
const specQualList = fieldDecl?.specifierQualifierList();
|
|
52
|
+
|
|
53
|
+
const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
|
|
54
|
+
// For simple types without struct specifier, getText() is used
|
|
55
|
+
// The specifierQualifierList has "unsigned" and "int" as separate tokens
|
|
56
|
+
// which are joined with spaces
|
|
57
|
+
expect(result).toBe("unsigned int");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should extract anonymous struct type with proper spacing", () => {
|
|
61
|
+
// This is the bug case from issue #875
|
|
62
|
+
const source = `typedef struct {
|
|
63
|
+
int value;
|
|
64
|
+
struct {
|
|
65
|
+
unsigned int flag_a: 1;
|
|
66
|
+
unsigned int flag_b: 1;
|
|
67
|
+
} flags;
|
|
68
|
+
} config_t;`;
|
|
69
|
+
const { tree } = HeaderParser.parseC(source);
|
|
70
|
+
|
|
71
|
+
const structSpec = tree
|
|
72
|
+
?.translationUnit()
|
|
73
|
+
?.externalDeclaration(0)
|
|
74
|
+
?.declaration()
|
|
75
|
+
?.declarationSpecifiers()
|
|
76
|
+
?.declarationSpecifier(1)
|
|
77
|
+
?.typeSpecifier()
|
|
78
|
+
?.structOrUnionSpecifier();
|
|
79
|
+
|
|
80
|
+
// Get the second field (flags) which is an anonymous struct
|
|
81
|
+
const flagsDecl = structSpec
|
|
82
|
+
?.structDeclarationList()
|
|
83
|
+
?.structDeclaration(1);
|
|
84
|
+
const specQualList = flagsDecl?.specifierQualifierList();
|
|
85
|
+
|
|
86
|
+
const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
|
|
87
|
+
|
|
88
|
+
// Should have proper spacing, not concatenated tokens like "struct{...}"
|
|
89
|
+
expect(result).toContain("struct {");
|
|
90
|
+
// Note: our reconstruction adds spaces around the colon, which is valid C
|
|
91
|
+
expect(result).toContain("unsigned int flag_a : 1;");
|
|
92
|
+
expect(result).toContain("unsigned int flag_b : 1;");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should extract anonymous union type with proper spacing", () => {
|
|
96
|
+
// Test union handling (code path: structOrUnion.Struct() returns false)
|
|
97
|
+
const source = `typedef struct {
|
|
98
|
+
union {
|
|
99
|
+
int as_int;
|
|
100
|
+
float as_float;
|
|
101
|
+
} value;
|
|
102
|
+
} variant_t;`;
|
|
103
|
+
const { tree } = HeaderParser.parseC(source);
|
|
104
|
+
|
|
105
|
+
const structSpec = tree
|
|
106
|
+
?.translationUnit()
|
|
107
|
+
?.externalDeclaration(0)
|
|
108
|
+
?.declaration()
|
|
109
|
+
?.declarationSpecifiers()
|
|
110
|
+
?.declarationSpecifier(1)
|
|
111
|
+
?.typeSpecifier()
|
|
112
|
+
?.structOrUnionSpecifier();
|
|
113
|
+
|
|
114
|
+
// Get the first field (value) which is an anonymous union
|
|
115
|
+
const valueDecl = structSpec
|
|
116
|
+
?.structDeclarationList()
|
|
117
|
+
?.structDeclaration(0);
|
|
118
|
+
const specQualList = valueDecl?.specifierQualifierList();
|
|
119
|
+
|
|
120
|
+
const result = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
|
|
121
|
+
|
|
122
|
+
// Should use "union" keyword and have proper spacing
|
|
123
|
+
expect(result).toContain("union {");
|
|
124
|
+
expect(result).toContain("int as_int;");
|
|
125
|
+
expect(result).toContain("float as_float;");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -1042,11 +1042,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1042
1042
|
}
|
|
1043
1043
|
|
|
1044
1044
|
/**
|
|
1045
|
-
* Validate
|
|
1045
|
+
* Validate condition is a boolean expression (ADR-027, Issue #884).
|
|
1046
1046
|
* Part of IOrchestrator interface (ADR-053 A3).
|
|
1047
1047
|
*/
|
|
1048
|
-
|
|
1049
|
-
|
|
1048
|
+
validateConditionIsBoolean(
|
|
1049
|
+
ctx: Parser.ExpressionContext,
|
|
1050
|
+
conditionType: string,
|
|
1051
|
+
): void {
|
|
1052
|
+
TypeValidator.validateConditionIsBoolean(ctx, conditionType);
|
|
1050
1053
|
}
|
|
1051
1054
|
|
|
1052
1055
|
/**
|
|
@@ -1563,8 +1566,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1563
1566
|
|
|
1564
1567
|
/**
|
|
1565
1568
|
* Issue #561: Analyze modifications in a parse tree without full code generation.
|
|
1566
|
-
* Used by
|
|
1567
|
-
* for cross-file const inference
|
|
1569
|
+
* Used by the transpile() pipeline to collect modification info from includes
|
|
1570
|
+
* for cross-file const inference.
|
|
1568
1571
|
*
|
|
1569
1572
|
* Issue #565: Now accepts optional cross-file data for transitive propagation.
|
|
1570
1573
|
* When a file calls a function from an included file that modifies its param,
|
|
@@ -3496,6 +3499,17 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3496
3499
|
|
|
3497
3500
|
// For C-Next/C structs, generate designated initializer
|
|
3498
3501
|
const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
|
|
3502
|
+
|
|
3503
|
+
// Issue #882: In C++ mode, anonymous structs/unions must use plain brace init.
|
|
3504
|
+
// Compound literals like (struct { ... }){ ... } create incompatible types in C++
|
|
3505
|
+
// because each struct { ... } definition creates a distinct nominal type.
|
|
3506
|
+
if (
|
|
3507
|
+
CodeGenState.cppMode &&
|
|
3508
|
+
(typeName.startsWith("struct {") || typeName.startsWith("union {"))
|
|
3509
|
+
) {
|
|
3510
|
+
return `{ ${fieldInits.join(", ")} }`;
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3499
3513
|
return `(${castType}){ ${fieldInits.join(", ")} }`;
|
|
3500
3514
|
}
|
|
3501
3515
|
|
|
@@ -3763,6 +3777,18 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3763
3777
|
return true;
|
|
3764
3778
|
}
|
|
3765
3779
|
|
|
3780
|
+
// Callback-compatible functions: struct params become pass-by-value
|
|
3781
|
+
// to match C function pointer typedef signatures
|
|
3782
|
+
if (
|
|
3783
|
+
CodeGenState.currentFunctionName &&
|
|
3784
|
+
CodeGenState.callbackCompatibleFunctions.has(
|
|
3785
|
+
CodeGenState.currentFunctionName,
|
|
3786
|
+
) &&
|
|
3787
|
+
this.isKnownStruct(typeName)
|
|
3788
|
+
) {
|
|
3789
|
+
return true;
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3766
3792
|
return false;
|
|
3767
3793
|
}
|
|
3768
3794
|
|
|
@@ -714,16 +714,19 @@ class TypeValidator {
|
|
|
714
714
|
}
|
|
715
715
|
|
|
716
716
|
// ========================================================================
|
|
717
|
-
//
|
|
717
|
+
// Condition Boolean Validation (ADR-027, Issue #884)
|
|
718
718
|
// ========================================================================
|
|
719
719
|
|
|
720
|
-
static
|
|
720
|
+
static validateConditionIsBoolean(
|
|
721
|
+
ctx: Parser.ExpressionContext,
|
|
722
|
+
conditionType: string,
|
|
723
|
+
): void {
|
|
721
724
|
const ternaryExpr = ctx.ternaryExpression();
|
|
722
725
|
const orExprs = ternaryExpr.orExpression();
|
|
723
726
|
|
|
724
727
|
if (orExprs.length !== 1) {
|
|
725
728
|
throw new Error(
|
|
726
|
-
`Error E0701:
|
|
729
|
+
`Error E0701: ${conditionType} condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`,
|
|
727
730
|
);
|
|
728
731
|
}
|
|
729
732
|
|
|
@@ -737,7 +740,7 @@ class TypeValidator {
|
|
|
737
740
|
const andExpr = orExpr.andExpression(0);
|
|
738
741
|
if (!andExpr) {
|
|
739
742
|
throw new Error(
|
|
740
|
-
`Error E0701:
|
|
743
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
|
|
741
744
|
);
|
|
742
745
|
}
|
|
743
746
|
|
|
@@ -748,7 +751,7 @@ class TypeValidator {
|
|
|
748
751
|
const equalityExpr = andExpr.equalityExpression(0);
|
|
749
752
|
if (!equalityExpr) {
|
|
750
753
|
throw new Error(
|
|
751
|
-
`Error E0701:
|
|
754
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
|
|
752
755
|
);
|
|
753
756
|
}
|
|
754
757
|
|
|
@@ -759,7 +762,7 @@ class TypeValidator {
|
|
|
759
762
|
const relationalExpr = equalityExpr.relationalExpression(0);
|
|
760
763
|
if (!relationalExpr) {
|
|
761
764
|
throw new Error(
|
|
762
|
-
`Error E0701:
|
|
765
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
|
|
763
766
|
);
|
|
764
767
|
}
|
|
765
768
|
|
|
@@ -773,7 +776,7 @@ class TypeValidator {
|
|
|
773
776
|
}
|
|
774
777
|
|
|
775
778
|
throw new Error(
|
|
776
|
-
`Error E0701:
|
|
779
|
+
`Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)\n help: use explicit comparison: ${text} > 0 or ${text} != 0`,
|
|
777
780
|
);
|
|
778
781
|
}
|
|
779
782
|
|
|
@@ -8776,7 +8776,7 @@ describe("CodeGenerator", () => {
|
|
|
8776
8776
|
expect(code).toContain(">> 4");
|
|
8777
8777
|
});
|
|
8778
8778
|
|
|
8779
|
-
it("should generate float bit range read with shadow variable", () => {
|
|
8779
|
+
it("should generate float bit range read with union shadow variable", () => {
|
|
8780
8780
|
const source = `
|
|
8781
8781
|
void test() {
|
|
8782
8782
|
f32 value <- 3.14;
|
|
@@ -8793,8 +8793,9 @@ describe("CodeGenerator", () => {
|
|
|
8793
8793
|
sourcePath: "test.cnx",
|
|
8794
8794
|
});
|
|
8795
8795
|
|
|
8796
|
-
// Float bit access uses
|
|
8797
|
-
expect(code).toContain("
|
|
8796
|
+
// Float bit access uses union-based type punning (MISRA 21.15 compliant)
|
|
8797
|
+
expect(code).toContain("union { float f; uint32_t u; }");
|
|
8798
|
+
expect(code).not.toContain("memcpy");
|
|
8798
8799
|
});
|
|
8799
8800
|
});
|
|
8800
8801
|
|
|
@@ -10750,7 +10751,7 @@ describe("CodeGenerator", () => {
|
|
|
10750
10751
|
});
|
|
10751
10752
|
|
|
10752
10753
|
describe("float bit range access helpers", () => {
|
|
10753
|
-
it("should generate float bit range read with
|
|
10754
|
+
it("should generate float bit range read with union", () => {
|
|
10754
10755
|
const source = `
|
|
10755
10756
|
void test() {
|
|
10756
10757
|
f32 value <- 3.14;
|
|
@@ -10767,11 +10768,12 @@ describe("CodeGenerator", () => {
|
|
|
10767
10768
|
sourcePath: "test.cnx",
|
|
10768
10769
|
});
|
|
10769
10770
|
|
|
10770
|
-
// Float bit access uses
|
|
10771
|
-
expect(code).toContain("
|
|
10771
|
+
// Float bit access uses union-based type punning (MISRA 21.15 compliant)
|
|
10772
|
+
expect(code).toContain("union { float f; uint32_t u; }");
|
|
10773
|
+
expect(code).not.toContain("memcpy");
|
|
10772
10774
|
});
|
|
10773
10775
|
|
|
10774
|
-
it("should generate f64 bit range read", () => {
|
|
10776
|
+
it("should generate f64 bit range read with union", () => {
|
|
10775
10777
|
const source = `
|
|
10776
10778
|
void test() {
|
|
10777
10779
|
f64 value <- 3.14159;
|
|
@@ -10788,7 +10790,9 @@ describe("CodeGenerator", () => {
|
|
|
10788
10790
|
sourcePath: "test.cnx",
|
|
10789
10791
|
});
|
|
10790
10792
|
|
|
10791
|
-
|
|
10793
|
+
// f64 uses double/uint64_t union
|
|
10794
|
+
expect(code).toContain("union { double f; uint64_t u; }");
|
|
10795
|
+
expect(code).not.toContain("memcpy");
|
|
10792
10796
|
});
|
|
10793
10797
|
});
|
|
10794
10798
|
|
|
@@ -12264,7 +12268,7 @@ describe("CodeGenerator", () => {
|
|
|
12264
12268
|
expect(code).toContain("flags");
|
|
12265
12269
|
});
|
|
12266
12270
|
|
|
12267
|
-
it("should handle float bit punning", () => {
|
|
12271
|
+
it("should handle float bit punning with union", () => {
|
|
12268
12272
|
const source = `
|
|
12269
12273
|
void test() {
|
|
12270
12274
|
f32 val <- 3.14;
|
|
@@ -12281,8 +12285,9 @@ describe("CodeGenerator", () => {
|
|
|
12281
12285
|
sourcePath: "test.cnx",
|
|
12282
12286
|
});
|
|
12283
12287
|
|
|
12284
|
-
// Float bit access uses
|
|
12285
|
-
expect(code).toContain("
|
|
12288
|
+
// Float bit access uses union-based type punning (MISRA 21.15 compliant)
|
|
12289
|
+
expect(code).toContain("union { float f; uint32_t u; }");
|
|
12290
|
+
expect(code).not.toContain("memcpy");
|
|
12286
12291
|
});
|
|
12287
12292
|
});
|
|
12288
12293
|
|
|
@@ -12870,7 +12875,7 @@ describe("CodeGenerator", () => {
|
|
|
12870
12875
|
|
|
12871
12876
|
describe("refactored helper methods", () => {
|
|
12872
12877
|
describe("float bit range access (PR #715 coverage)", () => {
|
|
12873
|
-
it("should generate f32 bit range read with shadow
|
|
12878
|
+
it("should generate f32 bit range read with union shadow", () => {
|
|
12874
12879
|
const source = `
|
|
12875
12880
|
void test() {
|
|
12876
12881
|
f32 floatVal <- 1.5;
|
|
@@ -12887,13 +12892,13 @@ describe("CodeGenerator", () => {
|
|
|
12887
12892
|
sourcePath: "test.cnx",
|
|
12888
12893
|
});
|
|
12889
12894
|
|
|
12890
|
-
// Should use
|
|
12895
|
+
// Should use union-based type punning (MISRA 21.15 compliant)
|
|
12891
12896
|
expect(code).toContain("__bits_floatVal");
|
|
12892
|
-
expect(code).toContain("
|
|
12893
|
-
expect(code).toContain("
|
|
12897
|
+
expect(code).toContain("union { float f; uint32_t u; }");
|
|
12898
|
+
expect(code).not.toContain("memcpy");
|
|
12894
12899
|
});
|
|
12895
12900
|
|
|
12896
|
-
it("should generate f64 bit range read with 64-bit shadow", () => {
|
|
12901
|
+
it("should generate f64 bit range read with 64-bit union shadow", () => {
|
|
12897
12902
|
const source = `
|
|
12898
12903
|
void test() {
|
|
12899
12904
|
f64 doubleVal <- 2.718;
|
|
@@ -12910,10 +12915,10 @@ describe("CodeGenerator", () => {
|
|
|
12910
12915
|
sourcePath: "test.cnx",
|
|
12911
12916
|
});
|
|
12912
12917
|
|
|
12913
|
-
// Should use 64-bit
|
|
12918
|
+
// Should use 64-bit union for f64 (MISRA 21.15 compliant)
|
|
12914
12919
|
expect(code).toContain("__bits_doubleVal");
|
|
12915
|
-
expect(code).toContain("uint64_t");
|
|
12916
|
-
expect(code).toContain("memcpy");
|
|
12920
|
+
expect(code).toContain("union { double f; uint64_t u; }");
|
|
12921
|
+
expect(code).not.toContain("memcpy");
|
|
12917
12922
|
});
|
|
12918
12923
|
|
|
12919
12924
|
it("should reuse shadow variable for repeated float bit reads", () => {
|
|
@@ -12934,11 +12939,13 @@ describe("CodeGenerator", () => {
|
|
|
12934
12939
|
sourcePath: "test.cnx",
|
|
12935
12940
|
});
|
|
12936
12941
|
|
|
12937
|
-
//
|
|
12938
|
-
const shadowDecls = (
|
|
12942
|
+
// Union shadow should only be declared once
|
|
12943
|
+
const shadowDecls = (
|
|
12944
|
+
code.match(/union { float f; uint32_t u; } __bits_val;/g) || []
|
|
12945
|
+
).length;
|
|
12939
12946
|
expect(shadowDecls).toBe(1);
|
|
12940
|
-
//
|
|
12941
|
-
expect(code).toContain("__bits_val");
|
|
12947
|
+
// Uses union member .u for bit access
|
|
12948
|
+
expect(code).toContain("__bits_val.u");
|
|
12942
12949
|
});
|
|
12943
12950
|
|
|
12944
12951
|
it("should generate integer bit range with start=0 optimization", () => {
|
|
@@ -13617,7 +13624,7 @@ describe("CodeGenerator", () => {
|
|
|
13617
13624
|
});
|
|
13618
13625
|
|
|
13619
13626
|
describe("float bit range edge cases", () => {
|
|
13620
|
-
it("should generate f32 bit range with non-zero start position", () => {
|
|
13627
|
+
it("should generate f32 bit range with non-zero start position using union", () => {
|
|
13621
13628
|
const source = `
|
|
13622
13629
|
void test() {
|
|
13623
13630
|
f32 value <- 3.14;
|
|
@@ -13636,7 +13643,9 @@ describe("CodeGenerator", () => {
|
|
|
13636
13643
|
|
|
13637
13644
|
expect(code).toContain("__bits_value");
|
|
13638
13645
|
expect(code).toContain(">> 8");
|
|
13639
|
-
|
|
13646
|
+
// Uses union, not memcpy (MISRA 21.15 compliant)
|
|
13647
|
+
expect(code).toContain("union { float f; uint32_t u; }");
|
|
13648
|
+
expect(code).not.toContain("memcpy");
|
|
13640
13649
|
});
|
|
13641
13650
|
|
|
13642
13651
|
it("should generate f32 bit range with start=0", () => {
|
|
@@ -13661,7 +13670,7 @@ describe("CodeGenerator", () => {
|
|
|
13661
13670
|
expect(code).toContain(">> 0U");
|
|
13662
13671
|
});
|
|
13663
13672
|
|
|
13664
|
-
it("should handle multiple reads from same float reusing shadow", () => {
|
|
13673
|
+
it("should handle multiple reads from same float reusing union shadow", () => {
|
|
13665
13674
|
const source = `
|
|
13666
13675
|
void test() {
|
|
13667
13676
|
f32 data <- 2.5;
|
|
@@ -13680,10 +13689,10 @@ describe("CodeGenerator", () => {
|
|
|
13680
13689
|
sourcePath: "test.cnx",
|
|
13681
13690
|
});
|
|
13682
13691
|
|
|
13683
|
-
//
|
|
13684
|
-
expect(code).toContain("uint32_t __bits_data");
|
|
13685
|
-
// Multiple reads should work
|
|
13686
|
-
expect(code).toContain("__bits_data");
|
|
13692
|
+
// Union shadow declaration should appear once
|
|
13693
|
+
expect(code).toContain("union { float f; uint32_t u; } __bits_data;");
|
|
13694
|
+
// Multiple reads should work via union member
|
|
13695
|
+
expect(code).toContain("__bits_data.u");
|
|
13687
13696
|
});
|
|
13688
13697
|
});
|
|
13689
13698
|
|
|
@@ -15406,7 +15415,7 @@ describe("CodeGenerator", () => {
|
|
|
15406
15415
|
expect(code).toContain("& 0xFF");
|
|
15407
15416
|
});
|
|
15408
15417
|
|
|
15409
|
-
it("should generate float bit range access with
|
|
15418
|
+
it("should generate float bit range access with union", () => {
|
|
15410
15419
|
const source = `
|
|
15411
15420
|
void test() {
|
|
15412
15421
|
f32 fval <- 1.5;
|
|
@@ -15423,11 +15432,13 @@ describe("CodeGenerator", () => {
|
|
|
15423
15432
|
sourcePath: "test.cnx",
|
|
15424
15433
|
});
|
|
15425
15434
|
|
|
15426
|
-
|
|
15427
|
-
expect(code).toContain("__bits_fval");
|
|
15435
|
+
// Uses union, not memcpy (MISRA 21.15 compliant)
|
|
15436
|
+
expect(code).toContain("union { float f; uint32_t u; } __bits_fval;");
|
|
15437
|
+
expect(code).toContain("__bits_fval.u");
|
|
15438
|
+
expect(code).not.toContain("memcpy");
|
|
15428
15439
|
});
|
|
15429
15440
|
|
|
15430
|
-
it("should generate f64 bit range access with uint64_t shadow", () => {
|
|
15441
|
+
it("should generate f64 bit range access with uint64_t union shadow", () => {
|
|
15431
15442
|
const source = `
|
|
15432
15443
|
void test() {
|
|
15433
15444
|
f64 dval <- 1.5;
|
|
@@ -15444,8 +15455,10 @@ describe("CodeGenerator", () => {
|
|
|
15444
15455
|
sourcePath: "test.cnx",
|
|
15445
15456
|
});
|
|
15446
15457
|
|
|
15447
|
-
|
|
15448
|
-
expect(code).toContain("
|
|
15458
|
+
// Uses union, not memcpy (MISRA 21.15 compliant)
|
|
15459
|
+
expect(code).toContain("union { double f; uint64_t u; } __bits_dval;");
|
|
15460
|
+
expect(code).toContain("__bits_dval.u");
|
|
15461
|
+
expect(code).not.toContain("memcpy");
|
|
15449
15462
|
});
|
|
15450
15463
|
});
|
|
15451
15464
|
});
|
|
@@ -312,7 +312,9 @@ describe("ExpressionWalker - const inference integration", () => {
|
|
|
312
312
|
}
|
|
313
313
|
`;
|
|
314
314
|
const transpiler = new Transpiler({ inputs: [] });
|
|
315
|
-
const transpileResult =
|
|
315
|
+
const transpileResult = (
|
|
316
|
+
await transpiler.transpile({ kind: "source", source: source })
|
|
317
|
+
).files[0];
|
|
316
318
|
expect(transpileResult.success).toBe(true);
|
|
317
319
|
// The caller function should NOT have const param because x is passed to modifyParam
|
|
318
320
|
// which modifies its parameter
|
|
@@ -340,7 +342,9 @@ describe("ExpressionWalker - const inference integration", () => {
|
|
|
340
342
|
}
|
|
341
343
|
`;
|
|
342
344
|
const transpiler = new Transpiler({ inputs: [] });
|
|
343
|
-
const transpileResult =
|
|
345
|
+
const transpileResult = (
|
|
346
|
+
await transpiler.transpile({ kind: "source", source: source })
|
|
347
|
+
).files[0];
|
|
344
348
|
expect(transpileResult.success).toBe(true);
|
|
345
349
|
// x should not be const because it's passed through a modifying call chain
|
|
346
350
|
expect(transpileResult.code).not.toContain("void caller(const uint32_t x)");
|
|
@@ -367,7 +371,9 @@ describe("ExpressionWalker - const inference integration", () => {
|
|
|
367
371
|
}
|
|
368
372
|
`;
|
|
369
373
|
const transpiler = new Transpiler({ inputs: [] });
|
|
370
|
-
const transpileResult =
|
|
374
|
+
const transpileResult = (
|
|
375
|
+
await transpiler.transpile({ kind: "source", source: source })
|
|
376
|
+
).files[0];
|
|
371
377
|
expect(transpileResult.success).toBe(true);
|
|
372
378
|
// x should not be const
|
|
373
379
|
expect(transpileResult.code).not.toContain("void caller(const uint32_t x)");
|