Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions _cmptest/testdata/sqlite3/3.49.1/sqlite3/llcppg.pub
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ sqlite3_context Context
sqlite3_destructor_type DestructorType
sqlite3_file File
sqlite3_filename Filename
sqlite3_index_constraint IndexConstraint
sqlite3_index_constraint_usage IndexConstraintUsage
sqlite3_index_info IndexInfo
sqlite3_index_orderby IndexOrderby
sqlite3_int64 Int64
sqlite3_io_methods IoMethods
sqlite3_loadext_entry LoadextEntry
Expand Down
13 changes: 10 additions & 3 deletions _cmptest/testdata/sqlite3/3.49.1/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -5689,15 +5689,22 @@ type Module struct {
}

type IndexConstraint struct {
Unused [8]uint8
IColumn c.Int
Op c.Char
Usable c.Char
ITermOffset c.Int
}

type IndexOrderby struct {
Unused [8]uint8
IColumn c.Int
Desc c.Char
}

/* Outputs */

type IndexConstraintUsage struct {
Unused [8]uint8
ArgvIndex c.Int
Omit c.Char
}

/*
Expand Down
61 changes: 43 additions & 18 deletions _xtool/internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,27 +242,18 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul

case clang.CursorClassDecl:
classDecl := ct.ProcessClassDecl(cursor)
// todo(zzy):class need consider nested struct situation
ct.file.Decls = append(ct.file.Decls, classDecl)
// class havent anonymous situation
ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name)
case clang.CursorStructDecl:
structDecl := ct.ProcessStructDecl(cursor)
ct.file.Decls = append(ct.file.Decls, structDecl)
decls := ct.ProcessStructDecl(cursor)
ct.file.Decls = append(ct.file.Decls, decls...)
ct.logf("visitTop: ProcessStructDecl END")
if structDecl.Name != nil {
ct.logln(structDecl.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorUnionDecl:
unionDecl := ct.ProcessUnionDecl(cursor)
ct.file.Decls = append(ct.file.Decls, unionDecl)
decls := ct.ProcessUnionDecl(cursor)
ct.file.Decls = append(ct.file.Decls, decls...)
ct.logf("visitTop: ProcessUnionDecl END")
if unionDecl.Name != nil {
ct.logln(unionDecl.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
// Handle functions and class methods (including out-of-class method)
// Example: void MyClass::myMethod() { ... } out-of-class method
Expand Down Expand Up @@ -759,12 +750,32 @@ func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
return methods
}

func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) []ast.Decl {
var decls []ast.Decl
ct.incIndent()
defer ct.decIndent()
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind)

childs := PostOrderVisitChildren(cursor, func(child, parent clang.Cursor) bool {
return (child.Kind == clang.CursorStructDecl || child.Kind == clang.CursorUnionDecl) && child.IsAnonymous() == 0
})

for _, child := range childs {
// Check if this is a named nested struct/union
typ := ct.ProcessRecordType(child)
// note(zzy):use len(typ.Fields.List) to ensure it has fields not a forward declaration
// but maybe make the forward decl in to AST is also good.
if child.IsAnonymous() == 0 && len(typ.Fields.List) > 0 {
childName := clang.GoString(child.String())
ct.logln("ProcessRecordDecl: Found named nested struct:", childName)
decls = append(decls, &ast.TypeDecl{
Object: ct.CreateObject(child, &ast.Ident{Name: childName}),
Type: ct.ProcessRecordType(child),
})
}
}

decl := &ast.TypeDecl{
Object: ct.CreateObject(cursor, nil),
Type: ct.ProcessRecordType(cursor),
Expand All @@ -778,14 +789,15 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
ct.logln("ProcessRecordDecl: is anonymous")
}

return decl
decls = append(decls, decl)
return decls
}

func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl {
func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) []ast.Decl {
return ct.ProcessRecordDecl(cursor)
}

func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl {
func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) []ast.Decl {
return ct.ProcessRecordDecl(cursor)
}

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

func PostOrderVisitChildren(cursor clang.Cursor, collect func(c, p clang.Cursor) bool) []clang.Cursor {
var children []clang.Cursor
clangutils.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult {
if collect(child, parent) {
childs := PostOrderVisitChildren(child, collect)
children = append(children, childs[:]...)
children = append(children, child)
}
return clang.ChildVisit_Continue
})
return children
}

func IsExplicitSigned(t clang.Type) bool {
return t.Kind == clang.TypeCharS || t.Kind == clang.TypeSChar
}
Expand Down
56 changes: 52 additions & 4 deletions _xtool/internal/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,27 @@ import (
"github.com/goplus/llgo/xtool/clang/preprocessor"
)

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

func testFrom(t *testing.T, dir string, filename string, gen bool) {
func TestParserCMode(t *testing.T) {
cases := []string{"named_nested_struct"}
for _, folder := range cases {
t.Run(folder, func(t *testing.T) {
testFrom(t, filepath.Join("testdata", folder), "temp.h", false, false)
})
}
}

func testFrom(t *testing.T, dir string, filename string, isCpp, gen bool) {
var expect string
var err error
if !gen {
Expand All @@ -42,7 +51,7 @@ func testFrom(t *testing.T, dir string, filename string, gen bool) {
}
ast, err := parser.Do(&parser.ConverterConfig{
File: filepath.Join(dir, filename),
IsCpp: true,
IsCpp: isCpp,
Args: []string{"-fparse-all-comments"},
})
if err != nil {
Expand Down Expand Up @@ -618,3 +627,42 @@ func compareOutput(t *testing.T, expected, actual string) {
t.Fatalf("Test failed: expected \n%s \ngot \n%s", expected, actual)
}
}

func TestPostOrderVisitChildren(t *testing.T) {
config := &clangutils.Config{
File: "./testdata/named_nested_struct/temp.h",
Temp: false,
IsCpp: false,
}

name := make(map[string]bool)
visit(config, func(cursor, parent clang.Cursor) clang.ChildVisitResult {
if cursor.Kind == clang.CursorStructDecl {
if !name[clang.GoString(cursor.String())] {
name[clang.GoString(cursor.String())] = true
file, line, column := clangutils.GetPresumedLocation(cursor.Location())
fmt.Println("StructDecl Name:", clang.GoString(cursor.String()), file, line, column)
}
}
return clang.ChildVisit_Recurse
})

index, unit, err := clangutils.CreateTranslationUnit(config)
if err != nil {
panic(err)
}
defer index.Dispose()
defer unit.Dispose()

childStr := make([]string, 6)
childs := parser.PostOrderVisitChildren(unit.Cursor(), func(child, parent clang.Cursor) bool {
return child.Kind == clang.CursorStructDecl
})
for i, child := range childs {
childStr[i] = clang.GoString(child.String())
}
expect := []string{"c", "d", "b", "f", "e", "a"}
if !reflect.DeepEqual(expect, childStr) {
fmt.Println("Unexpected child order:", childStr)
}
}
Loading
Loading