c-next 0.2.0 → 0.2.2

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 (37) hide show
  1. package/bin/cnext.js +17 -7
  2. package/dist/index.js +139747 -0
  3. package/dist/index.js.map +7 -0
  4. package/package.json +8 -4
  5. package/src/cli/Cli.ts +5 -2
  6. package/src/cli/PathNormalizer.ts +170 -0
  7. package/src/cli/PlatformIOCommand.ts +51 -3
  8. package/src/cli/__tests__/Cli.integration.test.ts +100 -0
  9. package/src/cli/__tests__/Cli.test.ts +17 -12
  10. package/src/cli/__tests__/PathNormalizer.test.ts +411 -0
  11. package/src/cli/__tests__/PlatformIOCommand.test.ts +156 -0
  12. package/src/cli/serve/__tests__/ServeCommand.test.ts +1 -1
  13. package/src/lib/__tests__/parseWithSymbols.test.ts +228 -0
  14. package/src/lib/parseCHeader.ts +5 -1
  15. package/src/lib/parseWithSymbols.ts +62 -5
  16. package/src/lib/types/ISymbolInfo.ts +4 -0
  17. package/src/lib/utils/SymbolPathUtils.ts +87 -0
  18. package/src/lib/utils/__tests__/SymbolPathUtils.test.ts +123 -0
  19. package/src/transpiler/NodeFileSystem.ts +5 -0
  20. package/src/transpiler/logic/symbols/SymbolTable.ts +17 -0
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +27 -32
  22. package/src/transpiler/output/codegen/TypeResolver.ts +12 -24
  23. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +130 -5
  24. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +67 -57
  25. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +9 -13
  26. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +20 -10
  27. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +5 -2
  28. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +18 -0
  29. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +25 -4
  30. package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +18 -0
  31. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +51 -2
  32. package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +76 -8
  33. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +147 -0
  34. package/src/transpiler/output/codegen/generators/expressions/__tests__/LiteralGenerator.test.ts +116 -0
  35. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +6 -5
  36. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +14 -5
  37. package/src/transpiler/types/IFileSystem.ts +8 -0
@@ -1807,40 +1807,24 @@ export default class CodeGenerator implements IOrchestrator {
1807
1807
  /**
1808
1808
  * Get struct field info for .length calculations.
1809
1809
  * Part of IOrchestrator interface.
1810
+ *
1811
+ * Issue #831: SymbolTable is the single source of truth for struct fields
1812
+ * (both C-Next and C header structs).
1810
1813
  */
1811
1814
  getStructFieldInfo(
1812
1815
  structType: string,
1813
1816
  fieldName: string,
1814
1817
  ): { type: string; dimensions?: (number | string)[] } | null {
1815
- // First check SymbolTable (C header structs)
1816
- if (CodeGenState.symbolTable) {
1817
- const fieldInfo = CodeGenState.symbolTable.getStructFieldInfo(
1818
- structType,
1819
- fieldName,
1820
- );
1821
- if (fieldInfo) {
1822
- return {
1823
- type: fieldInfo.type,
1824
- dimensions: fieldInfo.arrayDimensions,
1825
- };
1826
- }
1827
- }
1828
-
1829
- // Fall back to local C-Next struct fields
1830
- const localFields = CodeGenState.symbols!.structFields.get(structType);
1831
- if (localFields) {
1832
- const fieldType = localFields.get(fieldName);
1833
- if (fieldType) {
1834
- const fieldDimensions =
1835
- CodeGenState.symbols!.structFieldDimensions.get(structType);
1836
- const dimensions = fieldDimensions?.get(fieldName);
1837
- return {
1838
- type: fieldType,
1839
- dimensions: dimensions ? [...dimensions] : undefined,
1840
- };
1841
- }
1818
+ const fieldInfo = CodeGenState.symbolTable?.getStructFieldInfo(
1819
+ structType,
1820
+ fieldName,
1821
+ );
1822
+ if (fieldInfo) {
1823
+ return {
1824
+ type: fieldInfo.type,
1825
+ dimensions: fieldInfo.arrayDimensions,
1826
+ };
1842
1827
  }
1843
-
1844
1828
  return null;
1845
1829
  }
1846
1830
 
@@ -3451,14 +3435,25 @@ export default class CodeGenerator implements IOrchestrator {
3451
3435
  const isCppClass =
3452
3436
  CodeGenState.cppMode && this._isCppClassWithConstructor(typeName);
3453
3437
 
3438
+ // Issue #834: For named struct tags (no typedef), we need 'struct' prefix in C mode
3439
+ const needsStructKeyword =
3440
+ !CodeGenState.cppMode &&
3441
+ CodeGenState.symbolTable.checkNeedsStructKeyword(typeName);
3442
+ const castType = TypeGenerationHelper.generateUserType(
3443
+ typeName,
3444
+ needsStructKeyword,
3445
+ );
3446
+
3454
3447
  if (!fieldList) {
3455
3448
  // Empty initializer: Point {} -> (Point){ 0 } or {} for C++ classes
3456
- return isCppClass ? "{}" : `(${typeName}){ 0 }`;
3449
+ return isCppClass ? "{}" : `(${castType}){ 0 }`;
3457
3450
  }
3458
3451
 
3459
3452
  // Get field type info for nested initializers
3460
- // Issue #502: Check both local struct fields and SymbolTable (for C++ header structs)
3461
- const structFieldTypes = CodeGenState.symbols!.structFields.get(typeName);
3453
+ // Issue #831: SymbolTable is the single source of truth for struct fields
3454
+ // (both C-Next and C/C++ header structs)
3455
+ const structFieldTypes =
3456
+ CodeGenState.symbolTable?.getStructFieldTypes(typeName);
3462
3457
 
3463
3458
  const fields = fieldList.fieldInitializer().map((field) => {
3464
3459
  const fieldName = field.IDENTIFIER().getText();
@@ -3501,7 +3496,7 @@ export default class CodeGenerator implements IOrchestrator {
3501
3496
 
3502
3497
  // For C-Next/C structs, generate designated initializer
3503
3498
  const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
3504
- return `(${typeName}){ ${fieldInits.join(", ")} }`;
3499
+ return `(${castType}){ ${fieldInits.join(", ")} }`;
3505
3500
  }
3506
3501
 
3507
3502
  /**
@@ -457,36 +457,24 @@ class TypeResolver {
457
457
 
458
458
  /**
459
459
  * Get type info for a struct member field.
460
- * Issue #103: Checks SymbolTable first for C header structs.
460
+ * Issue #831: SymbolTable is the single source of truth for struct fields.
461
461
  */
462
462
  static getMemberTypeInfo(
463
463
  structType: string,
464
464
  memberName: string,
465
465
  ): { isArray: boolean; baseType: string } | undefined {
466
- if (CodeGenState.symbolTable) {
467
- const fieldInfo = CodeGenState.symbolTable.getStructFieldInfo(
468
- structType,
469
- memberName,
470
- );
471
- if (fieldInfo) {
472
- return {
473
- isArray:
474
- fieldInfo.arrayDimensions !== undefined &&
475
- fieldInfo.arrayDimensions.length > 0,
476
- baseType: fieldInfo.type,
477
- };
478
- }
479
- }
480
-
481
- const fieldType = CodeGenState.symbols?.structFields
482
- .get(structType)
483
- ?.get(memberName);
484
- if (!fieldType) return undefined;
485
-
486
- const arrayFields = CodeGenState.symbols?.structFieldArrays.get(structType);
487
- const isArray = arrayFields?.has(memberName) ?? false;
466
+ const fieldInfo = CodeGenState.symbolTable?.getStructFieldInfo(
467
+ structType,
468
+ memberName,
469
+ );
470
+ if (!fieldInfo) return undefined;
488
471
 
489
- return { isArray, baseType: fieldType };
472
+ return {
473
+ isArray:
474
+ fieldInfo.arrayDimensions !== undefined &&
475
+ fieldInfo.arrayDimensions.length > 0,
476
+ baseType: fieldInfo.type,
477
+ };
490
478
  }
491
479
  }
492
480
 
@@ -36,6 +36,8 @@ function setupGenerator(
36
36
 
37
37
  const symbolTable = new SymbolTable();
38
38
  const tSymbols = CNextResolver.resolve(tree, "test.cnx");
39
+ // Issue #831: Register TSymbols in SymbolTable (single source of truth)
40
+ symbolTable.addTSymbols(tSymbols);
39
41
  const symbols = TSymbolInfoAdapter.convert(tSymbols);
40
42
 
41
43
  const generator = new CodeGenerator();
@@ -105,7 +107,7 @@ describe("CodeGenerator Coverage Tests", () => {
105
107
  `;
106
108
  const { code } = setupGenerator(source);
107
109
  // Single string indexing returns a char, not a string
108
- expect(code).toContain("name[0]");
110
+ expect(code).toContain("name[0U]");
109
111
  });
110
112
 
111
113
  it("should return false for non-string array type", () => {
@@ -116,7 +118,7 @@ describe("CodeGenerator Coverage Tests", () => {
116
118
  }
117
119
  `;
118
120
  const { code } = setupGenerator(source);
119
- expect(code).toContain("values[0]");
121
+ expect(code).toContain("values[0U]");
120
122
  });
121
123
 
122
124
  it("should handle array access without typeInfo", () => {
@@ -627,8 +629,8 @@ describe("CodeGenerator Coverage Tests", () => {
627
629
  u8[2][3] matrix <- [[1, 2, 3], [4, 5, 6]];
628
630
  `;
629
631
  const { code } = setupGenerator(source);
630
- expect(code).toContain("{1, 2, 3}");
631
- expect(code).toContain("{4, 5, 6}");
632
+ expect(code).toContain("{1U, 2U, 3U}");
633
+ expect(code).toContain("{4U, 5U, 6U}");
632
634
  });
633
635
 
634
636
  it("should generate simple expression elements", () => {
@@ -636,7 +638,130 @@ describe("CodeGenerator Coverage Tests", () => {
636
638
  u32[5] values <- [10, 20, 30, 40, 50];
637
639
  `;
638
640
  const { code } = setupGenerator(source);
639
- expect(code).toContain("{10, 20, 30, 40, 50}");
641
+ expect(code).toContain("{10U, 20U, 30U, 40U, 50U}");
642
+ });
643
+ });
644
+
645
+ // ==========================================================================
646
+ // Issue #834: generateStructInitializer with named struct tags
647
+ // ==========================================================================
648
+ describe("generateStructInitializer() with named struct tags", () => {
649
+ it("should include struct keyword in compound literal for named struct tags", () => {
650
+ // Test the fix for issue #834: named struct tags need 'struct' prefix in cast
651
+ const source = `
652
+ struct NamedPoint { i32 x; i32 y; }
653
+ void test() {
654
+ NamedPoint p <- {x: 10, y: 20};
655
+ }
656
+ `;
657
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
658
+
659
+ const symbolTable = new SymbolTable();
660
+ // Mark NamedPoint as requiring 'struct' keyword (simulates C header import)
661
+ symbolTable.markNeedsStructKeyword("NamedPoint");
662
+
663
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
664
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
665
+
666
+ const generator = new CodeGenerator();
667
+ CodeGenState.symbolTable = symbolTable;
668
+ const code = generator.generate(tree, tokenStream, {
669
+ symbolInfo: symbols,
670
+ sourcePath: "test.cnx",
671
+ cppMode: false,
672
+ });
673
+
674
+ // The compound literal cast should include 'struct' keyword
675
+ expect(code).toContain("(struct NamedPoint)");
676
+ expect(code).toContain(".x = 10");
677
+ expect(code).toContain(".y = 20");
678
+ });
679
+
680
+ it("should include struct keyword in empty initializer via return statement", () => {
681
+ // Test the empty initializer path (line 3465) via return statement
682
+ // This is the only way to use explicit type syntax without expectedType context
683
+ const source = `
684
+ struct ReturnStruct { i32 value; }
685
+ ReturnStruct getEmpty() {
686
+ return ReturnStruct {};
687
+ }
688
+ `;
689
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
690
+
691
+ const symbolTable = new SymbolTable();
692
+ symbolTable.markNeedsStructKeyword("ReturnStruct");
693
+
694
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
695
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
696
+
697
+ const generator = new CodeGenerator();
698
+ CodeGenState.symbolTable = symbolTable;
699
+ const code = generator.generate(tree, tokenStream, {
700
+ symbolInfo: symbols,
701
+ sourcePath: "test.cnx",
702
+ cppMode: false,
703
+ });
704
+
705
+ // Empty initializer should have struct keyword: (struct ReturnStruct){ 0 }
706
+ expect(code).toContain("(struct ReturnStruct){ 0 }");
707
+ });
708
+
709
+ it("should NOT include struct keyword for typedef'd structs in C mode", () => {
710
+ // This tests the branch where checkNeedsStructKeyword returns false
711
+ const source = `
712
+ struct TypedefPoint { i32 x; i32 y; }
713
+ void test() {
714
+ TypedefPoint p <- {x: 1, y: 2};
715
+ }
716
+ `;
717
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
718
+
719
+ const symbolTable = new SymbolTable();
720
+ // Do NOT mark as needing struct keyword (simulates typedef'd struct)
721
+
722
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
723
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
724
+
725
+ const generator = new CodeGenerator();
726
+ CodeGenState.symbolTable = symbolTable;
727
+ const code = generator.generate(tree, tokenStream, {
728
+ symbolInfo: symbols,
729
+ sourcePath: "test.cnx",
730
+ cppMode: false,
731
+ });
732
+
733
+ // Should NOT have 'struct' keyword since it's not marked
734
+ expect(code).not.toContain("(struct TypedefPoint)");
735
+ expect(code).toContain("(TypedefPoint)");
736
+ });
737
+
738
+ it("should NOT include struct keyword in C++ mode", () => {
739
+ const source = `
740
+ struct CppPoint { i32 x; i32 y; }
741
+ void test() {
742
+ CppPoint p <- {x: 5, y: 10};
743
+ }
744
+ `;
745
+ const { tree, tokenStream } = CNextSourceParser.parse(source);
746
+
747
+ const symbolTable = new SymbolTable();
748
+ // Even if marked, C++ mode should not use struct keyword
749
+ symbolTable.markNeedsStructKeyword("CppPoint");
750
+
751
+ const tSymbols = CNextResolver.resolve(tree, "test.cnx");
752
+ const symbols = TSymbolInfoAdapter.convert(tSymbols);
753
+
754
+ const generator = new CodeGenerator();
755
+ CodeGenState.symbolTable = symbolTable;
756
+ const code = generator.generate(tree, tokenStream, {
757
+ symbolInfo: symbols,
758
+ sourcePath: "test.cnx",
759
+ cppMode: true,
760
+ });
761
+
762
+ // In C++ mode, struct keyword should NOT be in the cast
763
+ expect(code).not.toContain("(struct CppPoint)");
764
+ expect(code).toContain("(CppPoint)");
640
765
  });
641
766
  });
642
767