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.
- package/bin/cnext.js +17 -7
- package/dist/index.js +139747 -0
- package/dist/index.js.map +7 -0
- package/package.json +8 -4
- package/src/cli/Cli.ts +5 -2
- package/src/cli/PathNormalizer.ts +170 -0
- package/src/cli/PlatformIOCommand.ts +51 -3
- package/src/cli/__tests__/Cli.integration.test.ts +100 -0
- package/src/cli/__tests__/Cli.test.ts +17 -12
- package/src/cli/__tests__/PathNormalizer.test.ts +411 -0
- package/src/cli/__tests__/PlatformIOCommand.test.ts +156 -0
- package/src/cli/serve/__tests__/ServeCommand.test.ts +1 -1
- package/src/lib/__tests__/parseWithSymbols.test.ts +228 -0
- package/src/lib/parseCHeader.ts +5 -1
- package/src/lib/parseWithSymbols.ts +62 -5
- package/src/lib/types/ISymbolInfo.ts +4 -0
- package/src/lib/utils/SymbolPathUtils.ts +87 -0
- package/src/lib/utils/__tests__/SymbolPathUtils.test.ts +123 -0
- package/src/transpiler/NodeFileSystem.ts +5 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +17 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +27 -32
- package/src/transpiler/output/codegen/TypeResolver.ts +12 -24
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +130 -5
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +67 -57
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +9 -13
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +20 -10
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +5 -2
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +18 -0
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +25 -4
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/handlerTestUtils.ts +18 -0
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +51 -2
- package/src/transpiler/output/codegen/generators/expressions/LiteralGenerator.ts +76 -8
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +147 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/LiteralGenerator.test.ts +116 -0
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +6 -5
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +14 -5
- 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
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
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 ? "{}" : `(${
|
|
3449
|
+
return isCppClass ? "{}" : `(${castType}){ 0 }`;
|
|
3457
3450
|
}
|
|
3458
3451
|
|
|
3459
3452
|
// Get field type info for nested initializers
|
|
3460
|
-
// Issue #
|
|
3461
|
-
|
|
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 `(${
|
|
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 #
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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 {
|
|
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[
|
|
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[
|
|
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("{
|
|
631
|
-
expect(code).toContain("{
|
|
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("{
|
|
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
|
|