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.
Files changed (48) hide show
  1. package/README.md +59 -57
  2. package/dist/index.js +641 -191
  3. package/dist/index.js.map +4 -4
  4. package/package.json +1 -1
  5. package/src/cli/Runner.ts +1 -1
  6. package/src/cli/__tests__/Runner.test.ts +8 -8
  7. package/src/cli/serve/ServeCommand.ts +29 -9
  8. package/src/transpiler/Transpiler.ts +105 -200
  9. package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
  10. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
  11. package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
  12. package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
  13. package/src/transpiler/data/IncludeResolver.ts +11 -3
  14. package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
  15. package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
  16. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +170 -14
  17. package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
  18. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +327 -0
  19. package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
  20. package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
  21. package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
  22. package/src/transpiler/logic/symbols/c/index.ts +50 -1
  23. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +99 -2
  24. package/src/transpiler/logic/symbols/c/utils/__tests__/DeclaratorUtils.test.ts +128 -0
  25. package/src/transpiler/output/codegen/CodeGenerator.ts +31 -5
  26. package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
  27. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +49 -36
  28. package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
  29. package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +90 -25
  30. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
  31. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
  32. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
  33. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +23 -14
  34. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +10 -7
  35. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
  36. package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +10 -1
  37. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
  38. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +6 -3
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +36 -22
  40. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +9 -1
  41. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +8 -6
  42. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +34 -18
  43. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
  44. package/src/transpiler/state/CodeGenState.ts +6 -0
  45. package/src/transpiler/types/IPipelineFile.ts +2 -2
  46. package/src/transpiler/types/IPipelineInput.ts +1 -1
  47. package/src/transpiler/types/TTranspileInput.ts +18 -0
  48. package/src/utils/constants/TypeConstants.ts +22 -0
@@ -48,7 +48,7 @@ describe("Transpiler coverage tests", () => {
48
48
  mockFs,
49
49
  );
50
50
 
51
- const result = await transpiler.run();
51
+ const result = await transpiler.transpile({ kind: "files" });
52
52
 
53
53
  expect(result.success).toBe(true);
54
54
  // Check output file has .cpp extension
@@ -62,9 +62,12 @@ describe("Transpiler coverage tests", () => {
62
62
  mockFs,
63
63
  );
64
64
 
65
- const result = await transpiler.transpileSource(
66
- "void test(u32 value) { u32 x <- value; }",
67
- );
65
+ const result = (
66
+ await transpiler.transpile({
67
+ kind: "source",
68
+ source: "void test(u32 value) { u32 x <- value; }",
69
+ })
70
+ ).files[0];
68
71
 
69
72
  expect(result.success).toBe(true);
70
73
  // C++ mode generates different output (const T& vs const T*)
@@ -95,7 +98,7 @@ describe("Transpiler coverage tests", () => {
95
98
  mockFs,
96
99
  );
97
100
 
98
- const result = await transpiler.run();
101
+ const result = await transpiler.transpile({ kind: "files" });
99
102
 
100
103
  expect(result.success).toBe(true);
101
104
  // Should generate .cpp file since C++ was detected
@@ -123,7 +126,7 @@ describe("Transpiler coverage tests", () => {
123
126
  mockFs,
124
127
  );
125
128
 
126
- const result = await transpiler.run();
129
+ const result = await transpiler.transpile({ kind: "files" });
127
130
 
128
131
  expect(result.success).toBe(true);
129
132
  // .hpp triggers C++ mode
@@ -160,7 +163,7 @@ describe("Transpiler coverage tests", () => {
160
163
  mockFs,
161
164
  );
162
165
 
163
- await transpiler.run();
166
+ await transpiler.transpile({ kind: "files" });
164
167
 
165
168
  // Debug mode should produce console output
166
169
  expect(consoleSpy).toHaveBeenCalled();
@@ -204,7 +207,7 @@ describe("Transpiler coverage tests", () => {
204
207
  mockFs,
205
208
  );
206
209
 
207
- const result = await transpiler.run();
210
+ const result = await transpiler.transpile({ kind: "files" });
208
211
 
209
212
  expect(result.success).toBe(true);
210
213
  // Pure C header should result in .c output (not .cpp)
@@ -232,7 +235,7 @@ describe("Transpiler coverage tests", () => {
232
235
  mockFs,
233
236
  );
234
237
 
235
- const result = await transpiler.run();
238
+ const result = await transpiler.transpile({ kind: "files" });
236
239
 
237
240
  expect(result.success).toBe(true);
238
241
  });
@@ -264,7 +267,7 @@ describe("Transpiler coverage tests", () => {
264
267
  mockFs,
265
268
  );
266
269
 
267
- const result = await transpiler.run();
270
+ const result = await transpiler.transpile({ kind: "files" });
268
271
 
269
272
  expect(result.success).toBe(true);
270
273
  });
@@ -288,7 +291,7 @@ describe("Transpiler coverage tests", () => {
288
291
  mockFs,
289
292
  );
290
293
 
291
- const result = await transpiler.run();
294
+ const result = await transpiler.transpile({ kind: "files" });
292
295
 
293
296
  expect(result.success).toBe(true);
294
297
  // In parse-only mode, no output files should be written
@@ -307,9 +310,12 @@ describe("Transpiler coverage tests", () => {
307
310
 
308
311
  // Mock an internal failure by transpiling invalid code that passes parsing
309
312
  // but fails in a later stage
310
- const result = await transpiler.transpileSource(
311
- "void test() { unknownType x; }",
312
- );
313
+ const result = (
314
+ await transpiler.transpile({
315
+ kind: "source",
316
+ source: "void test() { unknownType x; }",
317
+ })
318
+ ).files[0];
313
319
 
314
320
  // Should handle gracefully
315
321
  expect(result).toBeDefined();
@@ -321,7 +327,12 @@ describe("Transpiler coverage tests", () => {
321
327
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
322
328
 
323
329
  // Test with valid code to ensure normal path works
324
- const result = await transpiler.transpileSource("void main() { }");
330
+ const result = (
331
+ await transpiler.transpile({
332
+ kind: "source",
333
+ source: "void main() { }",
334
+ })
335
+ ).files[0];
325
336
  expect(result.success).toBe(true);
326
337
  });
327
338
  });
@@ -348,7 +359,7 @@ describe("Transpiler coverage tests", () => {
348
359
  mockFs,
349
360
  );
350
361
 
351
- const result = await transpiler.run();
362
+ const result = await transpiler.transpile({ kind: "files" });
352
363
 
353
364
  // Should succeed with distinct function names
354
365
  expect(result.success).toBe(true);
@@ -380,7 +391,7 @@ describe("Transpiler coverage tests", () => {
380
391
  mockFs,
381
392
  );
382
393
 
383
- const result = await transpiler.run();
394
+ const result = await transpiler.transpile({ kind: "files" });
384
395
 
385
396
  // This test exercises the symbol conflict detection path (lines 240-256)
386
397
  // Duplicate function definitions across included files trigger conflicts
@@ -416,7 +427,7 @@ describe("Transpiler coverage tests", () => {
416
427
  mockFs,
417
428
  );
418
429
 
419
- const result = await transpiler.run();
430
+ const result = await transpiler.transpile({ kind: "files" });
420
431
 
421
432
  // Should fail due to code generation error
422
433
  expect(result.success).toBe(false);
@@ -451,7 +462,7 @@ describe("Transpiler coverage tests", () => {
451
462
  mockFs,
452
463
  );
453
464
 
454
- const result = await transpiler.run();
465
+ const result = await transpiler.transpile({ kind: "files" });
455
466
 
456
467
  expect(result.success).toBe(true);
457
468
  });
@@ -484,7 +495,7 @@ describe("Transpiler coverage tests", () => {
484
495
  mockFs,
485
496
  );
486
497
 
487
- const result = await transpiler.run();
498
+ const result = await transpiler.transpile({ kind: "files" });
488
499
 
489
500
  expect(result.success).toBe(true);
490
501
  // Check that contributions were processed
@@ -509,7 +520,7 @@ describe("Transpiler coverage tests", () => {
509
520
  mockFs,
510
521
  );
511
522
 
512
- const result = await transpiler.run();
523
+ const result = await transpiler.transpile({ kind: "files" });
513
524
 
514
525
  expect(result.success).toBe(true);
515
526
  // No header file should be generated
@@ -541,7 +552,7 @@ describe("Transpiler coverage tests", () => {
541
552
  mockFs,
542
553
  );
543
554
 
544
- const result = await transpiler.run();
555
+ const result = await transpiler.transpile({ kind: "files" });
545
556
 
546
557
  expect(result.success).toBe(true);
547
558
  // Header should be generated
@@ -562,7 +573,12 @@ describe("Transpiler coverage tests", () => {
562
573
  mockFs,
563
574
  );
564
575
 
565
- const result = await transpiler.transpileSource("void main() { }");
576
+ const result = (
577
+ await transpiler.transpile({
578
+ kind: "source",
579
+ source: "void main() { }",
580
+ })
581
+ ).files[0];
566
582
 
567
583
  expect(result.success).toBe(true);
568
584
  });
@@ -585,14 +601,19 @@ describe("Transpiler coverage tests", () => {
585
601
  );
586
602
 
587
603
  // Simpler code that's known to work
588
- const result = await transpiler.transpileSource(`
604
+ const result = (
605
+ await transpiler.transpile({
606
+ kind: "source",
607
+ source: `
589
608
  void modifyValue(u32 value) {
590
609
  u32 x <- value + 1;
591
610
  }
592
611
  void main() {
593
612
  modifyValue(42);
594
613
  }
595
- `);
614
+ `,
615
+ })
616
+ ).files[0];
596
617
 
597
618
  expect(result.success).toBe(true);
598
619
  expect(result.code).toContain("modifyValue");
@@ -625,7 +646,7 @@ describe("Transpiler coverage tests", () => {
625
646
  mockFs,
626
647
  );
627
648
 
628
- const result = await transpiler.run();
649
+ const result = await transpiler.transpile({ kind: "files" });
629
650
 
630
651
  // Should complete (possibly with warnings) rather than crash
631
652
  expect(result).toBeDefined();
@@ -665,7 +686,12 @@ describe("Transpiler coverage tests", () => {
665
686
  );
666
687
 
667
688
  // transpileSource standalone should flush cache
668
- const result = await transpiler.transpileSource("void main() { }");
689
+ const result = (
690
+ await transpiler.transpile({
691
+ kind: "source",
692
+ source: "void main() { }",
693
+ })
694
+ ).files[0];
669
695
 
670
696
  expect(result.success).toBe(true);
671
697
  });
@@ -698,7 +724,7 @@ describe("Transpiler coverage tests", () => {
698
724
  mockFs,
699
725
  );
700
726
 
701
- const result = await transpiler.run();
727
+ const result = await transpiler.transpile({ kind: "files" });
702
728
 
703
729
  expect(result.success).toBe(true);
704
730
  // File contribution should exist
@@ -717,12 +743,17 @@ describe("Transpiler coverage tests", () => {
717
743
 
718
744
  // Code with MISRA violation - function call in if condition (Rule 13.5)
719
745
  // Note: This may or may not trigger depending on analyzer config
720
- const result = await transpiler.transpileSource(`
746
+ const result = (
747
+ await transpiler.transpile({
748
+ kind: "source",
749
+ source: `
721
750
  bool isReady() { return 1 = 1; }
722
751
  void test() {
723
752
  if (isReady() && isReady()) { }
724
753
  }
725
- `);
754
+ `,
755
+ })
756
+ ).files[0];
726
757
 
727
758
  // Result should be defined regardless of success/failure
728
759
  expect(result).toBeDefined();
@@ -733,12 +764,17 @@ describe("Transpiler coverage tests", () => {
733
764
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
734
765
 
735
766
  // Code that triggers initialization analyzer error
736
- const result = await transpiler.transpileSource(`
767
+ const result = (
768
+ await transpiler.transpile({
769
+ kind: "source",
770
+ source: `
737
771
  void test() {
738
772
  u32 x;
739
773
  u32 y <- x;
740
774
  }
741
- `);
775
+ `,
776
+ })
777
+ ).files[0];
742
778
 
743
779
  expect(result.success).toBe(false);
744
780
  expect(result.errors.length).toBeGreaterThan(0);
@@ -776,12 +812,12 @@ describe("Transpiler coverage tests", () => {
776
812
  };
777
813
 
778
814
  const transpiler1 = new Transpiler(config, mockFs);
779
- const result1 = await transpiler1.run();
815
+ const result1 = await transpiler1.transpile({ kind: "files" });
780
816
  expect(result1.success).toBe(true);
781
817
 
782
818
  // Second run uses cache but still detects C++
783
819
  const transpiler2 = new Transpiler(config, mockFs);
784
- const result2 = await transpiler2.run();
820
+ const result2 = await transpiler2.transpile({ kind: "files" });
785
821
  expect(result2.success).toBe(true);
786
822
  // Should still generate .cpp output
787
823
  const writeCalls = mockFs.getWriteLog();
@@ -808,11 +844,11 @@ describe("Transpiler coverage tests", () => {
808
844
 
809
845
  // First run
810
846
  const transpiler1 = new Transpiler(config, mockFs);
811
- await transpiler1.run();
847
+ await transpiler1.transpile({ kind: "files" });
812
848
 
813
849
  // Second run - cache hit path for .hpp
814
850
  const transpiler2 = new Transpiler(config, mockFs);
815
- const result2 = await transpiler2.run();
851
+ const result2 = await transpiler2.transpile({ kind: "files" });
816
852
  expect(result2.success).toBe(true);
817
853
 
818
854
  // Should generate .cpp
@@ -849,7 +885,7 @@ describe("Transpiler coverage tests", () => {
849
885
  mockFs,
850
886
  );
851
887
 
852
- await transpiler.run();
888
+ await transpiler.transpile({ kind: "files" });
853
889
 
854
890
  // Should have debug log for C++ header
855
891
  const debugCalls = consoleSpy.mock.calls.filter((call) =>
@@ -893,7 +929,7 @@ describe("Transpiler coverage tests", () => {
893
929
 
894
930
  // First run - populates cache and detects C++
895
931
  const transpiler1 = new Transpiler(config, mockFs);
896
- const result1 = await transpiler1.run();
932
+ const result1 = await transpiler1.transpile({ kind: "files" });
897
933
  expect(result1.success).toBe(true);
898
934
 
899
935
  // First run should detect C++ and output .cpp
@@ -906,7 +942,7 @@ describe("Transpiler coverage tests", () => {
906
942
  // Second run - should use cache AND still detect C++
907
943
  // This tests lines 543-547 (CHeader cache hit with C++ detection)
908
944
  const transpiler2 = new Transpiler(config, mockFs);
909
- const result2 = await transpiler2.run();
945
+ const result2 = await transpiler2.transpile({ kind: "files" });
910
946
  expect(result2.success).toBe(true);
911
947
 
912
948
  // Should output .cpp file (C++ detected even from cache)
@@ -935,7 +971,7 @@ describe("Transpiler coverage tests", () => {
935
971
 
936
972
  // First run - populates cache
937
973
  const transpiler1 = new Transpiler(config, mockFs);
938
- const result1 = await transpiler1.run();
974
+ const result1 = await transpiler1.transpile({ kind: "files" });
939
975
  expect(result1.success).toBe(true);
940
976
 
941
977
  // First run should detect C++ from .hpp and output .cpp
@@ -948,7 +984,7 @@ describe("Transpiler coverage tests", () => {
948
984
  // Second run - should use cache and still set cppDetected from hpp file
949
985
  // This tests lines 548-550 (CppHeader cache hit)
950
986
  const transpiler2 = new Transpiler(config, mockFs);
951
- const result2 = await transpiler2.run();
987
+ const result2 = await transpiler2.transpile({ kind: "files" });
952
988
  expect(result2.success).toBe(true);
953
989
 
954
990
  // Should output .cpp file
@@ -994,7 +1030,7 @@ describe("Transpiler coverage integration tests", () => {
994
1030
  noCache: true,
995
1031
  });
996
1032
 
997
- const result = await transpiler.run();
1033
+ const result = await transpiler.transpile({ kind: "files" });
998
1034
 
999
1035
  expect(result.success).toBe(true);
1000
1036
  // Should have processed all 3 files
@@ -1016,7 +1052,7 @@ describe("Transpiler coverage integration tests", () => {
1016
1052
  noCache: true,
1017
1053
  });
1018
1054
 
1019
- const result = await transpiler.run();
1055
+ const result = await transpiler.transpile({ kind: "files" });
1020
1056
 
1021
1057
  expect(result.success).toBe(true);
1022
1058
  // Should find files in subdirectories too
@@ -1057,14 +1093,14 @@ describe("Transpiler coverage integration tests", () => {
1057
1093
 
1058
1094
  // First run - populates cache
1059
1095
  const transpiler1 = new Transpiler(config);
1060
- const result1 = await transpiler1.run();
1096
+ const result1 = await transpiler1.transpile({ kind: "files" });
1061
1097
  expect(result1.success).toBe(true);
1062
1098
  // First run should detect C++ and output .cpp
1063
1099
  expect(result1.outputFiles.some((f) => f.endsWith(".cpp"))).toBe(true);
1064
1100
 
1065
1101
  // Second run - should use cache and still detect C++ (lines 543-547)
1066
1102
  const transpiler2 = new Transpiler(config);
1067
- const result2 = await transpiler2.run();
1103
+ const result2 = await transpiler2.transpile({ kind: "files" });
1068
1104
  expect(result2.success).toBe(true);
1069
1105
  // Should still output .cpp from cache hit path
1070
1106
  expect(result2.outputFiles.some((f) => f.endsWith(".cpp"))).toBe(true);
@@ -1097,13 +1133,13 @@ describe("Transpiler coverage integration tests", () => {
1097
1133
 
1098
1134
  // First run - populates cache
1099
1135
  const transpiler1 = new Transpiler(config);
1100
- const result1 = await transpiler1.run();
1136
+ const result1 = await transpiler1.transpile({ kind: "files" });
1101
1137
  expect(result1.success).toBe(true);
1102
1138
  expect(result1.outputFiles.some((f) => f.endsWith(".cpp"))).toBe(true);
1103
1139
 
1104
1140
  // Second run - should use cache and still detect C++ from .hpp (lines 548-550)
1105
1141
  const transpiler2 = new Transpiler(config);
1106
- const result2 = await transpiler2.run();
1142
+ const result2 = await transpiler2.transpile({ kind: "files" });
1107
1143
  expect(result2.success).toBe(true);
1108
1144
  expect(result2.outputFiles.some((f) => f.endsWith(".cpp"))).toBe(true);
1109
1145
  });
@@ -1134,7 +1170,7 @@ describe("Transpiler coverage integration tests", () => {
1134
1170
  noCache: true,
1135
1171
  });
1136
1172
 
1137
- const result = await transpiler.run();
1173
+ const result = await transpiler.transpile({ kind: "files" });
1138
1174
 
1139
1175
  expect(result.success).toBe(true);
1140
1176
  expect(result.files.length).toBe(2);
@@ -1161,12 +1197,12 @@ describe("Transpiler coverage integration tests", () => {
1161
1197
 
1162
1198
  // First run - should populate cache
1163
1199
  const transpiler1 = new Transpiler(config);
1164
- const result1 = await transpiler1.run();
1200
+ const result1 = await transpiler1.transpile({ kind: "files" });
1165
1201
  expect(result1.success).toBe(true);
1166
1202
 
1167
1203
  // Second run - should use cache
1168
1204
  const transpiler2 = new Transpiler(config);
1169
- const result2 = await transpiler2.run();
1205
+ const result2 = await transpiler2.transpile({ kind: "files" });
1170
1206
  expect(result2.success).toBe(true);
1171
1207
  });
1172
1208
 
@@ -1193,7 +1229,7 @@ describe("Transpiler coverage integration tests", () => {
1193
1229
  noCache: true,
1194
1230
  });
1195
1231
 
1196
- const result = await transpiler.run();
1232
+ const result = await transpiler.transpile({ kind: "files" });
1197
1233
 
1198
1234
  expect(result.success).toBe(true);
1199
1235
  // Should generate output files