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.
Files changed (104) hide show
  1. package/README.md +86 -63
  2. package/grammar/CNext.g4 +3 -17
  3. package/package.json +1 -1
  4. package/src/cli/serve/ServeCommand.ts +57 -45
  5. package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
  6. package/src/transpiler/Transpiler.ts +603 -613
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
  8. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
  9. package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
  10. package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
  11. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
  12. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
  13. package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
  14. package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
  15. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
  16. package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
  17. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  20. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
  22. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  23. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  24. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  25. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
  26. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  27. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  28. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  29. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  30. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
  31. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
  32. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
  33. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  34. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
  35. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
  36. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
  37. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
  38. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
  39. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
  41. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  42. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
  43. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  44. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  45. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
  46. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
  47. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
  48. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
  49. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  50. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  51. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  52. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
  53. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
  54. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  55. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
  56. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  57. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
  58. package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
  59. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
  60. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  61. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  62. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  63. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
  64. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  65. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
  66. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  67. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
  68. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  69. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  70. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  71. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  72. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  73. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  74. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
  75. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  76. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
  77. package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
  78. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  79. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  80. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  81. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  82. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  83. package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
  84. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
  85. package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
  86. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
  87. package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
  88. package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
  89. package/src/transpiler/types/IFileResult.ts +0 -4
  90. package/src/transpiler/types/IPipelineFile.ts +27 -0
  91. package/src/transpiler/types/IPipelineInput.ts +23 -0
  92. package/src/transpiler/types/TranspilerState.ts +1 -1
  93. package/src/utils/FormatUtils.ts +28 -2
  94. package/src/utils/MapUtils.ts +25 -0
  95. package/src/utils/PostfixAnalysisUtils.ts +48 -0
  96. package/src/utils/__tests__/FormatUtils.test.ts +42 -0
  97. package/src/utils/__tests__/MapUtils.test.ts +85 -0
  98. package/src/utils/constants/OperatorMappings.ts +19 -0
  99. package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
  100. package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
  101. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  102. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
  103. package/src/transpiler/types/ITranspileContext.ts +0 -49
  104. package/src/transpiler/types/ITranspileContribution.ts +0 -32
@@ -21,6 +21,7 @@ interface ISymbol {
21
21
  */
22
22
  interface ISymbolTable {
23
23
  getOverloads(name: string): ISymbol[];
24
+ getStructFields?(name: string): unknown;
24
25
  }
25
26
 
26
27
  class SymbolLookupHelper {
@@ -126,6 +127,49 @@ class SymbolLookupHelper {
126
127
  [ESourceLanguage.CNext],
127
128
  );
128
129
  }
130
+
131
+ /**
132
+ * Check if a function is a C-Next function (combined local + symbol table lookup).
133
+ * Checks local knownFunctions set first, then falls back to symbol table.
134
+ */
135
+ static isCNextFunctionCombined(
136
+ knownFunctions: ReadonlySet<string> | undefined,
137
+ symbolTable: ISymbolTable | null | undefined,
138
+ name: string,
139
+ ): boolean {
140
+ if (knownFunctions?.has(name)) return true;
141
+ return SymbolLookupHelper.isCNextFunction(symbolTable, name);
142
+ }
143
+
144
+ /**
145
+ * Check if a name is a known scope (combined local + symbol table lookup).
146
+ * Checks local knownScopes set first, then falls back to symbol table.
147
+ */
148
+ static isKnownScope(
149
+ knownScopes: ReadonlySet<string> | undefined,
150
+ symbolTable: ISymbolTable | null | undefined,
151
+ name: string,
152
+ ): boolean {
153
+ if (knownScopes?.has(name)) return true;
154
+ return SymbolLookupHelper.isNamespace(symbolTable, name);
155
+ }
156
+
157
+ /**
158
+ * Check if a type is a known struct (combined local + symbol table lookup).
159
+ * Checks local knownStructs and knownBitmaps, then falls back to symbol table.
160
+ * Issue #551: Bitmaps are struct-like (use pass-by-reference with -> access).
161
+ */
162
+ static isKnownStruct(
163
+ knownStructs: ReadonlySet<string> | undefined,
164
+ knownBitmaps: ReadonlySet<string> | undefined,
165
+ symbolTable: ISymbolTable | null | undefined,
166
+ typeName: string,
167
+ ): boolean {
168
+ if (knownStructs?.has(typeName)) return true;
169
+ if (knownBitmaps?.has(typeName)) return true;
170
+ if (symbolTable?.getStructFields?.(typeName)) return true;
171
+ return false;
172
+ }
129
173
  }
130
174
 
131
175
  export default SymbolLookupHelper;
@@ -242,6 +242,15 @@ class TypeGenerationHelper {
242
242
  return "string";
243
243
  }
244
244
 
245
+ // Bug fix: Handle arrayType syntax (u16[8] myArray) - check inner primitive type
246
+ if (ctx.arrayType()) {
247
+ const arrCtx = ctx.arrayType()!;
248
+ if (arrCtx.primitiveType()) {
249
+ const type = arrCtx.primitiveType()!.getText();
250
+ return TypeGenerationHelper.generatePrimitiveType(type).include;
251
+ }
252
+ }
253
+
245
254
  return null;
246
255
  }
247
256
  }
@@ -0,0 +1,479 @@
1
+ /**
2
+ * Unit tests for ArrayAccessHelper utility.
3
+ * Tests array access code generation patterns without ANTLR dependencies.
4
+ */
5
+ import { describe, it, expect, vi, beforeEach } from "vitest";
6
+ import ArrayAccessHelper from "../ArrayAccessHelper";
7
+ import IArrayAccessInfo from "../../types/IArrayAccessInfo";
8
+ import IArrayAccessDeps from "../../types/IArrayAccessDeps";
9
+
10
+ /**
11
+ * Create mock dependencies for testing.
12
+ */
13
+ function createMockDeps(): IArrayAccessDeps {
14
+ return {
15
+ generateBitMask: vi.fn().mockReturnValue("0xFF"),
16
+ requireInclude: vi.fn(),
17
+ isInFunctionBody: vi.fn().mockReturnValue(true),
18
+ registerFloatShadow: vi.fn().mockReturnValue(true),
19
+ isShadowCurrent: vi.fn().mockReturnValue(false),
20
+ markShadowCurrent: vi.fn(),
21
+ };
22
+ }
23
+
24
+ describe("ArrayAccessHelper", () => {
25
+ let mockDeps: IArrayAccessDeps;
26
+
27
+ beforeEach(() => {
28
+ mockDeps = createMockDeps();
29
+ });
30
+
31
+ describe("generate", () => {
32
+ it("should route to single-index for single-index accessType", () => {
33
+ const info: IArrayAccessInfo = {
34
+ rawName: "arr",
35
+ resolvedName: "arr",
36
+ accessType: "single-index",
37
+ indexExpr: "i",
38
+ line: 1,
39
+ };
40
+
41
+ const result = ArrayAccessHelper.generate(info, mockDeps);
42
+ expect(result).toBe("arr[i]");
43
+ });
44
+
45
+ it("should route to bit-range for bit-range accessType", () => {
46
+ const info: IArrayAccessInfo = {
47
+ rawName: "value",
48
+ resolvedName: "value",
49
+ accessType: "bit-range",
50
+ startExpr: "8",
51
+ widthExpr: "8",
52
+ typeInfo: {
53
+ baseType: "u32",
54
+ bitWidth: 32,
55
+ isArray: false,
56
+ isConst: false,
57
+ },
58
+ line: 1,
59
+ };
60
+
61
+ const result = ArrayAccessHelper.generate(info, mockDeps);
62
+ expect(result).toBe("((value >> 8) & 0xFF)");
63
+ });
64
+ });
65
+
66
+ describe("generateSingleIndex", () => {
67
+ it("should generate simple array access", () => {
68
+ const info: IArrayAccessInfo = {
69
+ rawName: "arr",
70
+ resolvedName: "arr",
71
+ accessType: "single-index",
72
+ indexExpr: "i",
73
+ line: 1,
74
+ };
75
+
76
+ expect(ArrayAccessHelper.generateSingleIndex(info)).toBe("arr[i]");
77
+ });
78
+
79
+ it("should use resolved name for dereferenced parameters", () => {
80
+ const info: IArrayAccessInfo = {
81
+ rawName: "param",
82
+ resolvedName: "(*param)",
83
+ accessType: "single-index",
84
+ indexExpr: "0",
85
+ line: 1,
86
+ };
87
+
88
+ expect(ArrayAccessHelper.generateSingleIndex(info)).toBe("(*param)[0]");
89
+ });
90
+
91
+ it("should handle complex index expressions", () => {
92
+ const info: IArrayAccessInfo = {
93
+ rawName: "data",
94
+ resolvedName: "data",
95
+ accessType: "single-index",
96
+ indexExpr: "i * 2 + offset",
97
+ line: 1,
98
+ };
99
+
100
+ expect(ArrayAccessHelper.generateSingleIndex(info)).toBe(
101
+ "data[i * 2 + offset]",
102
+ );
103
+ });
104
+
105
+ it("should handle array with numeric literal index", () => {
106
+ const info: IArrayAccessInfo = {
107
+ rawName: "buffer",
108
+ resolvedName: "buffer",
109
+ accessType: "single-index",
110
+ indexExpr: "42",
111
+ line: 1,
112
+ };
113
+
114
+ expect(ArrayAccessHelper.generateSingleIndex(info)).toBe("buffer[42]");
115
+ });
116
+ });
117
+
118
+ describe("validateNotBitmap", () => {
119
+ it("should throw for bitmap bracket indexing", () => {
120
+ const info: IArrayAccessInfo = {
121
+ rawName: "flags",
122
+ resolvedName: "flags",
123
+ accessType: "single-index",
124
+ indexExpr: "3",
125
+ typeInfo: {
126
+ baseType: "u8",
127
+ bitWidth: 8,
128
+ isArray: false,
129
+ isConst: false,
130
+ isBitmap: true,
131
+ bitmapTypeName: "Flags",
132
+ },
133
+ line: 10,
134
+ };
135
+
136
+ expect(() => ArrayAccessHelper.validateNotBitmap(info)).toThrow("bitmap");
137
+ expect(() => ArrayAccessHelper.validateNotBitmap(info)).toThrow("Flags");
138
+ expect(() => ArrayAccessHelper.validateNotBitmap(info)).toThrow(
139
+ "line 10",
140
+ );
141
+ });
142
+
143
+ it("should not throw for regular arrays", () => {
144
+ const info: IArrayAccessInfo = {
145
+ rawName: "arr",
146
+ resolvedName: "arr",
147
+ accessType: "single-index",
148
+ indexExpr: "0",
149
+ typeInfo: {
150
+ baseType: "u8",
151
+ bitWidth: 8,
152
+ isArray: true,
153
+ isConst: false,
154
+ },
155
+ line: 1,
156
+ };
157
+
158
+ expect(() => ArrayAccessHelper.validateNotBitmap(info)).not.toThrow();
159
+ });
160
+
161
+ it("should not throw when no typeInfo is provided", () => {
162
+ const info: IArrayAccessInfo = {
163
+ rawName: "unknown",
164
+ resolvedName: "unknown",
165
+ accessType: "single-index",
166
+ indexExpr: "0",
167
+ line: 1,
168
+ };
169
+
170
+ expect(() => ArrayAccessHelper.validateNotBitmap(info)).not.toThrow();
171
+ });
172
+
173
+ it("should not throw for bitmap without bitmapTypeName", () => {
174
+ const info: IArrayAccessInfo = {
175
+ rawName: "flags",
176
+ resolvedName: "flags",
177
+ accessType: "single-index",
178
+ indexExpr: "0",
179
+ typeInfo: {
180
+ baseType: "u8",
181
+ bitWidth: 8,
182
+ isArray: false,
183
+ isConst: false,
184
+ isBitmap: true,
185
+ // No bitmapTypeName
186
+ },
187
+ line: 1,
188
+ };
189
+
190
+ expect(() => ArrayAccessHelper.validateNotBitmap(info)).not.toThrow();
191
+ });
192
+ });
193
+
194
+ describe("isFloatBitRange", () => {
195
+ it("should return true for f32", () => {
196
+ expect(ArrayAccessHelper.isFloatBitRange({ baseType: "f32" })).toBe(true);
197
+ });
198
+
199
+ it("should return true for f64", () => {
200
+ expect(ArrayAccessHelper.isFloatBitRange({ baseType: "f64" })).toBe(true);
201
+ });
202
+
203
+ it("should return false for u32", () => {
204
+ expect(ArrayAccessHelper.isFloatBitRange({ baseType: "u32" })).toBe(
205
+ false,
206
+ );
207
+ });
208
+
209
+ it("should return false for i64", () => {
210
+ expect(ArrayAccessHelper.isFloatBitRange({ baseType: "i64" })).toBe(
211
+ false,
212
+ );
213
+ });
214
+
215
+ it("should return false for undefined typeInfo", () => {
216
+ expect(ArrayAccessHelper.isFloatBitRange(undefined)).toBe(false);
217
+ });
218
+
219
+ it("should return false for empty object", () => {
220
+ expect(ArrayAccessHelper.isFloatBitRange({})).toBe(false);
221
+ });
222
+ });
223
+
224
+ describe("generateBitRange", () => {
225
+ it("should route to integer bit range for u32", () => {
226
+ const info: IArrayAccessInfo = {
227
+ rawName: "value",
228
+ resolvedName: "value",
229
+ accessType: "bit-range",
230
+ startExpr: "8",
231
+ widthExpr: "8",
232
+ typeInfo: {
233
+ baseType: "u32",
234
+ bitWidth: 32,
235
+ isArray: false,
236
+ isConst: false,
237
+ },
238
+ line: 1,
239
+ };
240
+
241
+ const result = ArrayAccessHelper.generateBitRange(info, mockDeps);
242
+ expect(result).toBe("((value >> 8) & 0xFF)");
243
+ expect(mockDeps.generateBitMask).toHaveBeenCalledWith("8");
244
+ });
245
+
246
+ it("should route to float bit range for f32", () => {
247
+ const info: IArrayAccessInfo = {
248
+ rawName: "fval",
249
+ resolvedName: "fval",
250
+ accessType: "bit-range",
251
+ startExpr: "0",
252
+ widthExpr: "8",
253
+ typeInfo: {
254
+ baseType: "f32",
255
+ bitWidth: 32,
256
+ isArray: false,
257
+ isConst: false,
258
+ },
259
+ line: 1,
260
+ };
261
+
262
+ const result = ArrayAccessHelper.generateBitRange(info, mockDeps);
263
+ expect(result).toContain("memcpy");
264
+ expect(mockDeps.requireInclude).toHaveBeenCalledWith("string");
265
+ expect(mockDeps.requireInclude).toHaveBeenCalledWith(
266
+ "float_static_assert",
267
+ );
268
+ });
269
+ });
270
+
271
+ describe("generateIntegerBitRange", () => {
272
+ it("should generate bit range read with shift", () => {
273
+ const info: IArrayAccessInfo = {
274
+ rawName: "value",
275
+ resolvedName: "value",
276
+ accessType: "bit-range",
277
+ startExpr: "8",
278
+ widthExpr: "8",
279
+ typeInfo: {
280
+ baseType: "u32",
281
+ bitWidth: 32,
282
+ isArray: false,
283
+ isConst: false,
284
+ },
285
+ line: 1,
286
+ };
287
+
288
+ const result = ArrayAccessHelper.generateIntegerBitRange(info, mockDeps);
289
+ expect(result).toBe("((value >> 8) & 0xFF)");
290
+ });
291
+
292
+ it("should generate bit range read without shift when start is 0", () => {
293
+ const info: IArrayAccessInfo = {
294
+ rawName: "flags",
295
+ resolvedName: "flags",
296
+ accessType: "bit-range",
297
+ startExpr: "0",
298
+ widthExpr: "4",
299
+ typeInfo: {
300
+ baseType: "u8",
301
+ bitWidth: 8,
302
+ isArray: false,
303
+ isConst: false,
304
+ },
305
+ line: 1,
306
+ };
307
+
308
+ const result = ArrayAccessHelper.generateIntegerBitRange(info, mockDeps);
309
+ expect(result).toBe("((flags) & 0xFF)");
310
+ });
311
+
312
+ it("should use resolved name for dereferenced parameters", () => {
313
+ const info: IArrayAccessInfo = {
314
+ rawName: "param",
315
+ resolvedName: "(*param)",
316
+ accessType: "bit-range",
317
+ startExpr: "4",
318
+ widthExpr: "4",
319
+ line: 1,
320
+ };
321
+
322
+ const result = ArrayAccessHelper.generateIntegerBitRange(info, mockDeps);
323
+ expect(result).toBe("(((*param) >> 4) & 0xFF)");
324
+ });
325
+
326
+ it("should handle missing startExpr with default", () => {
327
+ const info: IArrayAccessInfo = {
328
+ rawName: "value",
329
+ resolvedName: "value",
330
+ accessType: "bit-range",
331
+ widthExpr: "8",
332
+ line: 1,
333
+ };
334
+
335
+ const result = ArrayAccessHelper.generateIntegerBitRange(info, mockDeps);
336
+ expect(result).toBe("((value) & 0xFF)");
337
+ });
338
+ });
339
+
340
+ describe("generateFloatBitRange", () => {
341
+ it("should generate float bit range with memcpy", () => {
342
+ const info: IArrayAccessInfo = {
343
+ rawName: "fval",
344
+ resolvedName: "fval",
345
+ accessType: "bit-range",
346
+ startExpr: "0",
347
+ widthExpr: "8",
348
+ typeInfo: {
349
+ baseType: "f32",
350
+ bitWidth: 32,
351
+ isArray: false,
352
+ isConst: false,
353
+ },
354
+ line: 1,
355
+ };
356
+
357
+ const result = ArrayAccessHelper.generateFloatBitRange(info, mockDeps);
358
+
359
+ expect(result).toContain("memcpy(&__bits_fval, &fval, sizeof(fval))");
360
+ expect(result).toContain("__bits_fval & 0xFF");
361
+ expect(mockDeps.requireInclude).toHaveBeenCalledWith("string");
362
+ expect(mockDeps.requireInclude).toHaveBeenCalledWith(
363
+ "float_static_assert",
364
+ );
365
+ expect(mockDeps.registerFloatShadow).toHaveBeenCalledWith(
366
+ "__bits_fval",
367
+ "uint32_t",
368
+ );
369
+ expect(mockDeps.markShadowCurrent).toHaveBeenCalledWith("__bits_fval");
370
+ });
371
+
372
+ it("should use uint64_t shadow for f64", () => {
373
+ const info: IArrayAccessInfo = {
374
+ rawName: "dval",
375
+ resolvedName: "dval",
376
+ accessType: "bit-range",
377
+ startExpr: "0",
378
+ widthExpr: "8",
379
+ typeInfo: {
380
+ baseType: "f64",
381
+ bitWidth: 64,
382
+ isArray: false,
383
+ isConst: false,
384
+ },
385
+ line: 1,
386
+ };
387
+
388
+ (mockDeps.generateBitMask as ReturnType<typeof vi.fn>).mockReturnValue(
389
+ "0xFFULL",
390
+ );
391
+
392
+ ArrayAccessHelper.generateFloatBitRange(info, mockDeps);
393
+
394
+ expect(mockDeps.registerFloatShadow).toHaveBeenCalledWith(
395
+ "__bits_dval",
396
+ "uint64_t",
397
+ );
398
+ expect(mockDeps.generateBitMask).toHaveBeenCalledWith("8", true);
399
+ });
400
+
401
+ it("should skip memcpy when shadow is current", () => {
402
+ const info: IArrayAccessInfo = {
403
+ rawName: "fval",
404
+ resolvedName: "fval",
405
+ accessType: "bit-range",
406
+ startExpr: "8",
407
+ widthExpr: "8",
408
+ typeInfo: {
409
+ baseType: "f32",
410
+ bitWidth: 32,
411
+ isArray: false,
412
+ isConst: false,
413
+ },
414
+ line: 1,
415
+ };
416
+
417
+ (mockDeps.isShadowCurrent as ReturnType<typeof vi.fn>).mockReturnValue(
418
+ true,
419
+ );
420
+
421
+ const result = ArrayAccessHelper.generateFloatBitRange(info, mockDeps);
422
+
423
+ expect(result).not.toContain("memcpy");
424
+ expect(result).toBe("((__bits_fval >> 8) & 0xFF)");
425
+ });
426
+
427
+ it("should throw error at global scope", () => {
428
+ const info: IArrayAccessInfo = {
429
+ rawName: "globalFloat",
430
+ resolvedName: "globalFloat",
431
+ accessType: "bit-range",
432
+ startExpr: "0",
433
+ widthExpr: "8",
434
+ typeInfo: {
435
+ baseType: "f32",
436
+ bitWidth: 32,
437
+ isArray: false,
438
+ isConst: false,
439
+ },
440
+ line: 5,
441
+ };
442
+
443
+ (mockDeps.isInFunctionBody as ReturnType<typeof vi.fn>).mockReturnValue(
444
+ false,
445
+ );
446
+
447
+ expect(() =>
448
+ ArrayAccessHelper.generateFloatBitRange(info, mockDeps),
449
+ ).toThrow("Float bit indexing reads");
450
+ expect(() =>
451
+ ArrayAccessHelper.generateFloatBitRange(info, mockDeps),
452
+ ).toThrow("globalFloat");
453
+ expect(() =>
454
+ ArrayAccessHelper.generateFloatBitRange(info, mockDeps),
455
+ ).toThrow("global scope");
456
+ });
457
+
458
+ it("should handle missing typeInfo with default f32 behavior", () => {
459
+ const info: IArrayAccessInfo = {
460
+ rawName: "fval",
461
+ resolvedName: "fval",
462
+ accessType: "bit-range",
463
+ startExpr: "0",
464
+ widthExpr: "8",
465
+ line: 1,
466
+ };
467
+
468
+ ArrayAccessHelper.generateFloatBitRange(info, mockDeps);
469
+
470
+ // Without typeInfo, defaults to f32 behavior (uint32_t shadow)
471
+ expect(mockDeps.registerFloatShadow).toHaveBeenCalledWith(
472
+ "__bits_fval",
473
+ "uint32_t",
474
+ );
475
+ // is64Bit is false since baseType is undefined (not f64)
476
+ expect(mockDeps.generateBitMask).toHaveBeenCalledWith("8", false);
477
+ });
478
+ });
479
+ });