Skip to content

Commit 3f82aa9

Browse files
authored
feat(parser): nested struct/union decl (#516)
* parser:nested struct decl * test case * parser:refine anonymouse judge * update case * parser:avoid fetch forward decl to ast * verify ast.Node order fetch * parser:GetChilds with condition * preorder child list -> postorder traversal * cl:case with nest named struct * cl:case for #507 * GetChilds -> PostOrderVisitChildren
1 parent 23c8a9e commit 3f82aa9

11 files changed

Lines changed: 662 additions & 26 deletions

File tree

_cmptest/testdata/sqlite3/3.49.1/sqlite3/llcppg.pub

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ sqlite3_context Context
1515
sqlite3_destructor_type DestructorType
1616
sqlite3_file File
1717
sqlite3_filename Filename
18+
sqlite3_index_constraint IndexConstraint
19+
sqlite3_index_constraint_usage IndexConstraintUsage
1820
sqlite3_index_info IndexInfo
21+
sqlite3_index_orderby IndexOrderby
1922
sqlite3_int64 Int64
2023
sqlite3_io_methods IoMethods
2124
sqlite3_loadext_entry LoadextEntry

_cmptest/testdata/sqlite3/3.49.1/sqlite3/sqlite3.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5689,15 +5689,22 @@ type Module struct {
56895689
}
56905690

56915691
type IndexConstraint struct {
5692-
Unused [8]uint8
5692+
IColumn c.Int
5693+
Op c.Char
5694+
Usable c.Char
5695+
ITermOffset c.Int
56935696
}
56945697

56955698
type IndexOrderby struct {
5696-
Unused [8]uint8
5699+
IColumn c.Int
5700+
Desc c.Char
56975701
}
56985702

5703+
/* Outputs */
5704+
56995705
type IndexConstraintUsage struct {
5700-
Unused [8]uint8
5706+
ArgvIndex c.Int
5707+
Omit c.Char
57015708
}
57025709

57035710
/*

_xtool/internal/parser/parser.go

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -242,27 +242,18 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul
242242

243243
case clang.CursorClassDecl:
244244
classDecl := ct.ProcessClassDecl(cursor)
245+
// todo(zzy):class need consider nested struct situation
245246
ct.file.Decls = append(ct.file.Decls, classDecl)
246247
// class havent anonymous situation
247248
ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name)
248249
case clang.CursorStructDecl:
249-
structDecl := ct.ProcessStructDecl(cursor)
250-
ct.file.Decls = append(ct.file.Decls, structDecl)
250+
decls := ct.ProcessStructDecl(cursor)
251+
ct.file.Decls = append(ct.file.Decls, decls...)
251252
ct.logf("visitTop: ProcessStructDecl END")
252-
if structDecl.Name != nil {
253-
ct.logln(structDecl.Name.Name)
254-
} else {
255-
ct.logln("ANONY")
256-
}
257253
case clang.CursorUnionDecl:
258-
unionDecl := ct.ProcessUnionDecl(cursor)
259-
ct.file.Decls = append(ct.file.Decls, unionDecl)
254+
decls := ct.ProcessUnionDecl(cursor)
255+
ct.file.Decls = append(ct.file.Decls, decls...)
260256
ct.logf("visitTop: ProcessUnionDecl END")
261-
if unionDecl.Name != nil {
262-
ct.logln(unionDecl.Name.Name)
263-
} else {
264-
ct.logln("ANONY")
265-
}
266257
case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
267258
// Handle functions and class methods (including out-of-class method)
268259
// Example: void MyClass::myMethod() { ... } out-of-class method
@@ -759,12 +750,32 @@ func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
759750
return methods
760751
}
761752

762-
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
753+
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl {
754+
var decls []ast.Decl
763755
ct.incIndent()
764756
defer ct.decIndent()
765757
cursorName, cursorKind := getCursorDesc(cursor)
766758
ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
767759

760+
childs := PostOrderVisitChildren(cursor, func(child, parent clang.Cursor) bool {
761+
return (child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl) && child.IsAnonymous() == 0
762+
})
763+
764+
for _, child := range childs {
765+
// Check if this is a named nested struct/union
766+
typ := ct.ProcessRecordType(child)
767+
// note(zzy):use len(typ.Fields.List) to ensure it has fields not a forward declaration
768+
// but maybe make the forward decl in to AST is also good.
769+
if child.IsAnonymous() == 0 && len(typ.Fields.List) > 0 {
770+
childName := clang.GoString(child.String())
771+
ct.logln("ProcessRecordDecl: Found named nested struct:", childName)
772+
decls = append(decls, &ast.TypeDecl{
773+
Object: ct.CreateObject(child, &ast.Ident{Name: childName}),
774+
Type: ct.ProcessRecordType(child),
775+
})
776+
}
777+
}
778+
768779
decl := &ast.TypeDecl{
769780
Object: ct.CreateObject(cursor, nil),
770781
Type: ct.ProcessRecordType(cursor),
@@ -778,14 +789,15 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
778789
ct.logln("ProcessRecordDecl: is anonymous")
779790
}
780791

781-
return decl
792+
decls = append(decls, decl)
793+
return decls
782794
}
783795

784-
func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl {
796+
func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) []ast.Decl {
785797
return ct.ProcessRecordDecl(cursor)
786798
}
787799

788-
func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl {
800+
func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) []ast.Decl {
789801
return ct.ProcessRecordDecl(cursor)
790802
}
791803

@@ -959,6 +971,19 @@ func (ct *Converter) BuildScopingExpr(cursor clang.Cursor) ast.Expr {
959971
return buildScopingFromParts(parts)
960972
}
961973

974+
func PostOrderVisitChildren(cursor clang.Cursor, collect func(c, p clang.Cursor) bool) []clang.Cursor {
975+
var children []clang.Cursor
976+
clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult {
977+
if collect(child, parent) {
978+
childs := PostOrderVisitChildren(child, collect)
979+
children = append(children, childs[:]...)
980+
children = append(children, child)
981+
}
982+
return clang.ChildVisit_Continue
983+
})
984+
return children
985+
}
986+
962987
func IsExplicitSigned(t clang.Type) bool {
963988
return t.Kind == clang.TypeCharS || t.Kind == clang.TypeSChar
964989
}

_xtool/internal/parser/parser_test.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,27 @@ import (
1919
"github.com/goplus/llgo/xtool/clang/preprocessor"
2020
)
2121

22-
func TestParser(t *testing.T) {
22+
func TestParserCppMode(t *testing.T) {
2323
cases := []string{"class", "comment", "enum", "func", "scope", "struct", "typedef", "union", "macro", "forwarddecl1", "forwarddecl2", "include", "typeof"}
2424
// https://github.com/goplus/llgo/issues/1114
2525
// todo(zzy):use os.ReadDir
2626
for _, folder := range cases {
2727
t.Run(folder, func(t *testing.T) {
28-
testFrom(t, filepath.Join("testdata", folder), "temp.h", false)
28+
testFrom(t, filepath.Join("testdata", folder), "temp.h", true, false)
2929
})
3030
}
3131
}
3232

33-
func testFrom(t *testing.T, dir string, filename string, gen bool) {
33+
func TestParserCMode(t *testing.T) {
34+
cases := []string{"named_nested_struct"}
35+
for _, folder := range cases {
36+
t.Run(folder, func(t *testing.T) {
37+
testFrom(t, filepath.Join("testdata", folder), "temp.h", false, false)
38+
})
39+
}
40+
}
41+
42+
func testFrom(t *testing.T, dir string, filename string, isCpp, gen bool) {
3443
var expect string
3544
var err error
3645
if !gen {
@@ -42,7 +51,7 @@ func testFrom(t *testing.T, dir string, filename string, gen bool) {
4251
}
4352
ast, err := parser.Do(&parser.ConverterConfig{
4453
File: filepath.Join(dir, filename),
45-
IsCpp: true,
54+
IsCpp: isCpp,
4655
Args: []string{"-fparse-all-comments"},
4756
})
4857
if err != nil {
@@ -618,3 +627,42 @@ func compareOutput(t *testing.T, expected, actual string) {
618627
t.Fatalf("Test failed: expected \n%s \ngot \n%s", expected, actual)
619628
}
620629
}
630+
631+
func TestPostOrderVisitChildren(t *testing.T) {
632+
config := &clangutils.Config{
633+
File: "./testdata/named_nested_struct/temp.h",
634+
Temp: false,
635+
IsCpp: false,
636+
}
637+
638+
name := make(map[string]bool)
639+
visit(config, func(cursor, parent clang.Cursor) clang.ChildVisitResult {
640+
if cursor.Kind == clang.CursorStructDecl {
641+
if !name[clang.GoString(cursor.String())] {
642+
name[clang.GoString(cursor.String())] = true
643+
file, line, column := clangutils.GetPresumedLocation(cursor.Location())
644+
fmt.Println("StructDecl Name:", clang.GoString(cursor.String()), file, line, column)
645+
}
646+
}
647+
return clang.ChildVisit_Recurse
648+
})
649+
650+
index, unit, err := clangutils.CreateTranslationUnit(config)
651+
if err != nil {
652+
panic(err)
653+
}
654+
defer index.Dispose()
655+
defer unit.Dispose()
656+
657+
childStr := make([]string, 6)
658+
childs := parser.PostOrderVisitChildren(unit.Cursor(), func(child, parent clang.Cursor) bool {
659+
return child.Kind == clang.CursorStructDecl
660+
})
661+
for i, child := range childs {
662+
childStr[i] = clang.GoString(child.String())
663+
}
664+
expect := []string{"c", "d", "b", "f", "e", "a"}
665+
if !reflect.DeepEqual(expect, childStr) {
666+
fmt.Println("Unexpected child order:", childStr)
667+
}
668+
}

0 commit comments

Comments
 (0)