c-next 0.2.2 → 0.2.3
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/dist/index.js +105 -22
- package/dist/index.js.map +3 -3
- package/package.json +1 -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/__tests__/CodeGenerator.test.ts +49 -36
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +4 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +23 -14
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +9 -6
- package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +10 -1
- 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/__tests__/ArrayAccessHelper.test.ts +8 -6
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +34 -18
package/package.json
CHANGED
|
@@ -11,6 +11,9 @@ import type {
|
|
|
11
11
|
DeclarationSpecifiersContext,
|
|
12
12
|
StructOrUnionSpecifierContext,
|
|
13
13
|
EnumSpecifierContext,
|
|
14
|
+
StructDeclarationListContext,
|
|
15
|
+
StructDeclarationContext,
|
|
16
|
+
StructDeclaratorContext,
|
|
14
17
|
} from "../../../parser/c/grammar/CParser";
|
|
15
18
|
import SymbolUtils from "../../SymbolUtils";
|
|
16
19
|
import IExtractedParameter from "../../shared/IExtractedParameter";
|
|
@@ -227,8 +230,8 @@ class DeclaratorUtils {
|
|
|
227
230
|
// Use just the struct/union name, not "structName" concatenated
|
|
228
231
|
parts.push(identifier.getText());
|
|
229
232
|
} else {
|
|
230
|
-
// Anonymous struct -
|
|
231
|
-
parts.push(
|
|
233
|
+
// Anonymous struct - reconstruct with proper spacing
|
|
234
|
+
parts.push(DeclaratorUtils.reconstructAnonymousStruct(structSpec));
|
|
232
235
|
}
|
|
233
236
|
} else {
|
|
234
237
|
parts.push(typeSpec.getText());
|
|
@@ -246,6 +249,100 @@ class DeclaratorUtils {
|
|
|
246
249
|
return parts.join(" ") || "int";
|
|
247
250
|
}
|
|
248
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Reconstruct an anonymous struct/union type with proper spacing.
|
|
254
|
+
* For `struct { unsigned int flag_a: 1; }`, returns the properly formatted string
|
|
255
|
+
* instead of the concatenated tokens from getText().
|
|
256
|
+
*/
|
|
257
|
+
private static reconstructAnonymousStruct(
|
|
258
|
+
structSpec: StructOrUnionSpecifierContext,
|
|
259
|
+
): string {
|
|
260
|
+
const structOrUnion = structSpec.structOrUnion();
|
|
261
|
+
const keyword = structOrUnion.Struct() ? "struct" : "union";
|
|
262
|
+
|
|
263
|
+
const declList = structSpec.structDeclarationList();
|
|
264
|
+
if (!declList) {
|
|
265
|
+
return `${keyword} { }`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const fields = DeclaratorUtils.reconstructStructFields(declList);
|
|
269
|
+
return `${keyword} { ${fields} }`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Reconstruct struct fields with proper spacing.
|
|
274
|
+
*/
|
|
275
|
+
private static reconstructStructFields(
|
|
276
|
+
declList: StructDeclarationListContext,
|
|
277
|
+
): string {
|
|
278
|
+
const fieldStrings: string[] = [];
|
|
279
|
+
|
|
280
|
+
for (const decl of declList.structDeclaration()) {
|
|
281
|
+
const fieldStr = DeclaratorUtils.reconstructStructField(decl);
|
|
282
|
+
if (fieldStr) {
|
|
283
|
+
fieldStrings.push(fieldStr);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return fieldStrings.join(" ");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Reconstruct a single struct field declaration.
|
|
292
|
+
*/
|
|
293
|
+
private static reconstructStructField(
|
|
294
|
+
decl: StructDeclarationContext,
|
|
295
|
+
): string | null {
|
|
296
|
+
const specQualList = decl.specifierQualifierList();
|
|
297
|
+
if (!specQualList) return null;
|
|
298
|
+
|
|
299
|
+
// Get the base type with proper spacing
|
|
300
|
+
const baseType = DeclaratorUtils.extractTypeFromSpecQualList(specQualList);
|
|
301
|
+
|
|
302
|
+
const declaratorList = decl.structDeclaratorList();
|
|
303
|
+
if (!declaratorList) {
|
|
304
|
+
return `${baseType};`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Process each declarator in the list
|
|
308
|
+
const declarators: string[] = [];
|
|
309
|
+
for (const structDecl of declaratorList.structDeclarator()) {
|
|
310
|
+
const declStr = DeclaratorUtils.reconstructStructDeclarator(structDecl);
|
|
311
|
+
if (declStr) {
|
|
312
|
+
declarators.push(declStr);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (declarators.length === 0) {
|
|
317
|
+
return `${baseType};`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return `${baseType} ${declarators.join(", ")};`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Reconstruct a struct declarator (field name with optional bitfield width).
|
|
325
|
+
*/
|
|
326
|
+
private static reconstructStructDeclarator(
|
|
327
|
+
structDecl: StructDeclaratorContext,
|
|
328
|
+
): string | null {
|
|
329
|
+
const declarator = structDecl.declarator();
|
|
330
|
+
const hasColon = structDecl.Colon() !== null;
|
|
331
|
+
const constExpr = structDecl.constantExpression();
|
|
332
|
+
|
|
333
|
+
let name = "";
|
|
334
|
+
if (declarator) {
|
|
335
|
+
name = DeclaratorUtils.extractDeclaratorName(declarator) || "";
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (hasColon && constExpr) {
|
|
339
|
+
const width = constExpr.getText();
|
|
340
|
+
return `${name}: ${width}`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return name || null;
|
|
344
|
+
}
|
|
345
|
+
|
|
249
346
|
/**
|
|
250
347
|
* Extract typedef name from declaration specifiers.
|
|
251
348
|
* For "typedef struct { ... } AppConfig;", this returns "AppConfig".
|
|
@@ -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
|
+
});
|
|
@@ -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
|
});
|
|
@@ -133,7 +133,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
133
133
|
expect(result.code).toContain("sizeof(float)");
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
it("generates static assert for float bit indexing read", async () => {
|
|
136
|
+
it("generates static assert for float bit indexing read (no string.h)", async () => {
|
|
137
137
|
const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
|
|
138
138
|
|
|
139
139
|
const result = await transpiler.transpileSource(`
|
|
@@ -145,7 +145,9 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
145
145
|
|
|
146
146
|
expect(result.success).toBe(true);
|
|
147
147
|
expect(result.code).toContain("_Static_assert");
|
|
148
|
-
|
|
148
|
+
// Uses union-based type punning, no memcpy needed (MISRA 21.15 compliant)
|
|
149
|
+
expect(result.code).not.toContain("#include <string.h>");
|
|
150
|
+
expect(result.code).toContain("union { float f; uint32_t u; }");
|
|
149
151
|
});
|
|
150
152
|
});
|
|
151
153
|
|
|
@@ -2051,7 +2051,15 @@ interface IFloatBitRangeContext {
|
|
|
2051
2051
|
}
|
|
2052
2052
|
|
|
2053
2053
|
/**
|
|
2054
|
-
*
|
|
2054
|
+
* Get the C float type name for a C-Next float type.
|
|
2055
|
+
*/
|
|
2056
|
+
const getFloatTypeName = (baseType: string): string => {
|
|
2057
|
+
return baseType === "f64" ? "double" : "float";
|
|
2058
|
+
};
|
|
2059
|
+
|
|
2060
|
+
/**
|
|
2061
|
+
* Handle float bit range access with union-based type punning.
|
|
2062
|
+
* Uses union { float f; uint32_t u; } for MISRA C:2012 Rule 21.15 compliance.
|
|
2055
2063
|
*/
|
|
2056
2064
|
const handleFloatBitRange = (
|
|
2057
2065
|
ctx: IFloatBitRangeContext,
|
|
@@ -2065,35 +2073,36 @@ const handleFloatBitRange = (
|
|
|
2065
2073
|
);
|
|
2066
2074
|
}
|
|
2067
2075
|
|
|
2068
|
-
effects.push(
|
|
2069
|
-
{ type: "include", header: "string" },
|
|
2070
|
-
{ type: "include", header: "float_static_assert" },
|
|
2071
|
-
);
|
|
2076
|
+
effects.push({ type: "include", header: "float_static_assert" });
|
|
2072
2077
|
|
|
2073
2078
|
const isF64 = ctx.baseType === "f64";
|
|
2074
|
-
const
|
|
2079
|
+
const floatType = getFloatTypeName(ctx.baseType);
|
|
2080
|
+
const intType = isF64 ? "uint64_t" : "uint32_t";
|
|
2075
2081
|
const shadowName = `__bits_${ctx.rootIdentifier}`;
|
|
2076
2082
|
const mask = orchestrator.generateBitMask(ctx.width, isF64);
|
|
2077
2083
|
|
|
2078
2084
|
const needsDeclaration = !orchestrator.hasFloatBitShadow(shadowName);
|
|
2079
2085
|
if (needsDeclaration) {
|
|
2080
2086
|
orchestrator.registerFloatBitShadow(shadowName);
|
|
2081
|
-
|
|
2087
|
+
// Emit union declaration: union { float f; uint32_t u; } __bits_name;
|
|
2088
|
+
orchestrator.addPendingTempDeclaration(
|
|
2089
|
+
`union { ${floatType} f; ${intType} u; } ${shadowName};`,
|
|
2090
|
+
);
|
|
2082
2091
|
}
|
|
2083
2092
|
|
|
2084
2093
|
const shadowIsCurrent = orchestrator.isFloatShadowCurrent(shadowName);
|
|
2085
2094
|
orchestrator.markFloatShadowCurrent(shadowName);
|
|
2086
2095
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
}
|
|
2091
|
-
return `((${shadowName} >> ${ctx.start}) & ${mask})`;
|
|
2096
|
+
// If shadow is not current, emit assignment: __bits_name.f = floatVar;
|
|
2097
|
+
if (!shadowIsCurrent) {
|
|
2098
|
+
orchestrator.addPendingTempDeclaration(`${shadowName}.f = ${ctx.result};`);
|
|
2092
2099
|
}
|
|
2100
|
+
|
|
2101
|
+
// Return just the bit read expression using union member .u
|
|
2093
2102
|
if (ctx.start === "0") {
|
|
2094
|
-
return `(
|
|
2103
|
+
return `(${shadowName}.u & ${mask})`;
|
|
2095
2104
|
}
|
|
2096
|
-
return `(
|
|
2105
|
+
return `((${shadowName}.u >> ${ctx.start}) & ${mask})`;
|
|
2097
2106
|
};
|
|
2098
2107
|
|
|
2099
2108
|
// ========================================================================
|
|
@@ -1698,7 +1698,7 @@ describe("PostfixExpressionGenerator", () => {
|
|
|
1698
1698
|
});
|
|
1699
1699
|
|
|
1700
1700
|
describe("float bit indexing", () => {
|
|
1701
|
-
it("generates float bit range with
|
|
1701
|
+
it("generates float bit range with union-based type punning", () => {
|
|
1702
1702
|
const typeRegistry = new Map<string, TTypeInfo>([
|
|
1703
1703
|
[
|
|
1704
1704
|
"f",
|
|
@@ -1726,9 +1726,11 @@ describe("PostfixExpressionGenerator", () => {
|
|
|
1726
1726
|
});
|
|
1727
1727
|
|
|
1728
1728
|
const result = generatePostfixExpression(ctx, input, state, orchestrator);
|
|
1729
|
-
|
|
1730
|
-
expect(result.code).toContain("__bits_f");
|
|
1731
|
-
expect(result.
|
|
1729
|
+
// Uses union member .u for bit access, not memcpy
|
|
1730
|
+
expect(result.code).toContain("__bits_f.u");
|
|
1731
|
+
expect(result.code).not.toContain("memcpy");
|
|
1732
|
+
// No string.h needed for MISRA 21.15 compliance
|
|
1733
|
+
expect(result.effects).not.toContainEqual({
|
|
1732
1734
|
type: "include",
|
|
1733
1735
|
header: "string",
|
|
1734
1736
|
});
|
|
@@ -1767,7 +1769,7 @@ describe("PostfixExpressionGenerator", () => {
|
|
|
1767
1769
|
).toThrow("cannot be used at global scope");
|
|
1768
1770
|
});
|
|
1769
1771
|
|
|
1770
|
-
it("
|
|
1772
|
+
it("uses union member when shadow is current (no re-assignment)", () => {
|
|
1771
1773
|
const typeRegistry = new Map<string, TTypeInfo>([
|
|
1772
1774
|
[
|
|
1773
1775
|
"f",
|
|
@@ -1796,7 +1798,8 @@ describe("PostfixExpressionGenerator", () => {
|
|
|
1796
1798
|
|
|
1797
1799
|
const result = generatePostfixExpression(ctx, input, state, orchestrator);
|
|
1798
1800
|
expect(result.code).not.toContain("memcpy");
|
|
1799
|
-
|
|
1801
|
+
// Uses union member .u for bit access
|
|
1802
|
+
expect(result.code).toBe("(__bits_f.u & 0xFF)");
|
|
1800
1803
|
});
|
|
1801
1804
|
});
|
|
1802
1805
|
|
|
@@ -324,9 +324,10 @@ const generateSwitch = (
|
|
|
324
324
|
effects.push(...caseResult.effects);
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
// Generate default
|
|
327
|
+
// Generate default case
|
|
328
328
|
const defaultCtx = node.defaultCase();
|
|
329
329
|
if (defaultCtx) {
|
|
330
|
+
// Explicit default from source
|
|
330
331
|
const defaultResult = generateDefaultCase(
|
|
331
332
|
defaultCtx,
|
|
332
333
|
input,
|
|
@@ -335,6 +336,14 @@ const generateSwitch = (
|
|
|
335
336
|
);
|
|
336
337
|
lines.push(defaultResult.code);
|
|
337
338
|
effects.push(...defaultResult.effects);
|
|
339
|
+
} else {
|
|
340
|
+
// Issue #855: MISRA C:2012 Rule 16.4 - every switch shall have a default
|
|
341
|
+
// Generate empty default case for compliance
|
|
342
|
+
lines.push(
|
|
343
|
+
orchestrator.indent("default: {"),
|
|
344
|
+
orchestrator.indent(orchestrator.indent("break;")),
|
|
345
|
+
orchestrator.indent("}"),
|
|
346
|
+
);
|
|
338
347
|
}
|
|
339
348
|
|
|
340
349
|
lines.push("}");
|
|
@@ -70,8 +70,11 @@ class ArrayAccessHelper {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* Generate float bit range read with
|
|
74
|
-
* Uses
|
|
73
|
+
* Generate float bit range read with union-based type punning.
|
|
74
|
+
* Uses union { float f; uint32_t u; } for MISRA 21.15 compliance.
|
|
75
|
+
*
|
|
76
|
+
* NOTE: This helper is not used in production (PostfixExpressionGenerator
|
|
77
|
+
* handles float bit access directly). It exists for testing and completeness.
|
|
75
78
|
*/
|
|
76
79
|
static generateFloatBitRange(
|
|
77
80
|
info: IArrayAccessInfo,
|
|
@@ -85,7 +88,7 @@ class ArrayAccessHelper {
|
|
|
85
88
|
);
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
// No string.h needed - uses union-based type punning
|
|
89
92
|
deps.requireInclude("float_static_assert");
|
|
90
93
|
|
|
91
94
|
const isF64 = info.typeInfo?.baseType === "f64";
|