Merge pull request #812 from luoliwoshang/llcppsigfetch/refine

llcppsigfetch:cross-platform,log,fix bugs
This commit is contained in:
xushiwei
2024-10-28 10:54:33 +08:00
committed by GitHub
13 changed files with 752 additions and 241 deletions

View File

@@ -27,28 +27,73 @@ import (
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse" "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/args"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils" "github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config" "github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
) )
func main() { func main() {
cfgFile := "" ags, remainArgs := args.ParseArgs(os.Args[1:], map[string]bool{
outputToFile := false "--extract": true,
for i := 1; i < len(os.Args); i++ { })
arg := os.Args[i]
if arg == "--extract" { if ags.Help {
runExtract() printUsage()
return return
} else if arg == "--help" || arg == "-h" { }
printUsage() if ags.Verbose {
return parse.SetDebug(parse.DbgFlagAll)
} else if strings.HasPrefix(arg, "-out=") { }
outputToFile = parseBoolArg(arg, "out", false) extract := false
} else if cfgFile == "" && !strings.HasPrefix(arg, "-") { out := false
cfgFile = arg
var extractFile string
isTemp := false
isCpp := true
otherArgs := []string{}
for i := 0; i < len(remainArgs); i++ {
arg := remainArgs[i]
switch {
case arg == "--extract":
extract = true
if i+1 < len(remainArgs) && !strings.HasPrefix(remainArgs[i+1], "-") {
extractFile = remainArgs[i+1]
i++
} else {
fmt.Fprintln(os.Stderr, "Error: --extract requires a valid file argument")
printUsage()
os.Exit(1)
}
case strings.HasPrefix(arg, "-out="):
out = parseBoolArg(arg, "out", false)
case strings.HasPrefix(arg, "-temp="):
isTemp = parseBoolArg(arg, "temp", false)
case strings.HasPrefix(arg, "-cpp="):
isCpp = parseBoolArg(arg, "cpp", true)
default:
otherArgs = append(otherArgs, arg)
} }
} }
runFromConfig(cfgFile, outputToFile)
if extract {
if ags.Verbose {
fmt.Fprintln(os.Stderr, "runExtract: extractFile:", extractFile)
fmt.Fprintln(os.Stderr, "isTemp:", isTemp)
fmt.Fprintln(os.Stderr, "isCpp:", isCpp)
fmt.Fprintln(os.Stderr, "out:", out)
fmt.Fprintln(os.Stderr, "otherArgs:", otherArgs)
}
runExtract(extractFile, isTemp, isCpp, out, otherArgs, ags.Verbose)
} else {
if ags.Verbose {
fmt.Fprintln(os.Stderr, "runFromConfig: config file:", ags.CfgFile)
fmt.Fprintln(os.Stderr, "use stdin:", ags.UseStdin)
fmt.Fprintln(os.Stderr, "output to file:", out)
}
runFromConfig(ags.CfgFile, ags.UseStdin, out, ags.Verbose)
}
} }
func printUsage() { func printUsage() {
@@ -79,18 +124,21 @@ func printUsage() {
fmt.Println("Note: The two usage modes are mutually exclusive. Use either [<config_file>] OR --extract, not both.") fmt.Println("Note: The two usage modes are mutually exclusive. Use either [<config_file>] OR --extract, not both.")
} }
func runFromConfig(cfgFile string, outputToFile bool) { func runFromConfig(cfgFile string, useStdin bool, outputToFile bool, verbose bool) {
if cfgFile == "" {
cfgFile = "llcppg.cfg"
}
var data []byte var data []byte
var err error var err error
if cfgFile == "-" { if useStdin {
data, err = io.ReadAll(os.Stdin) data, err = io.ReadAll(os.Stdin)
} else { } else {
data, err = os.ReadFile(cfgFile) data, err = os.ReadFile(cfgFile)
} }
if verbose {
if useStdin {
fmt.Fprintln(os.Stderr, "runFromConfig: read from stdin")
} else {
fmt.Fprintln(os.Stderr, "runFromConfig: read from file", cfgFile)
}
}
check(err) check(err)
conf, err := config.GetConf(data) conf, err := config.GetConf(data)
@@ -99,9 +147,20 @@ func runFromConfig(cfgFile string, outputToFile bool) {
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile)
os.Exit(1)
} }
files := getHeaderFiles(conf.CFlags, conf.Include) //todo(zzy): reuse the llcppsymg's cflags parse
cflag := ParseCFlags(conf.CFlags)
files, notFounds, err := cflag.GenHeaderFilePaths(conf.Include)
check(err)
if verbose {
fmt.Fprintln(os.Stderr, "runFromConfig: header file paths", files)
if len(notFounds) > 0 {
fmt.Fprintln(os.Stderr, "runFromConfig: not found header files", notFounds)
}
}
context := parse.NewContext(conf.Cplusplus) context := parse.NewContext(conf.Cplusplus)
err = context.ProcessFiles(files) err = context.ProcessFiles(files)
@@ -110,48 +169,20 @@ func runFromConfig(cfgFile string, outputToFile bool) {
outputInfo(context, outputToFile) outputInfo(context, outputToFile)
} }
func runExtract() { func runExtract(file string, isTemp bool, isCpp bool, outToFile bool, otherArgs []string, verbose bool) {
if len(os.Args) < 3 {
fmt.Println("Error: Insufficient arguments for --extract")
printUsage()
os.Exit(1)
}
cfg := &clangutils.Config{ cfg := &clangutils.Config{
File: os.Args[2], File: file,
Args: []string{}, Args: otherArgs,
IsCpp: true, IsCpp: isCpp,
Temp: false, Temp: isTemp,
} }
outputToFile := false
for i := 3; i < len(os.Args); i++ {
arg := os.Args[i]
switch {
case strings.HasPrefix(arg, "-temp="):
cfg.Temp = parseBoolArg(arg, "temp", false)
os.Args = append(os.Args[:i], os.Args[i+1:]...)
i--
case strings.HasPrefix(arg, "-cpp="):
cfg.IsCpp = parseBoolArg(arg, "cpp", true)
os.Args = append(os.Args[:i], os.Args[i+1:]...)
i--
case strings.HasPrefix(arg, "-out="):
outputToFile = parseBoolArg(arg, "out", false)
os.Args = append(os.Args[:i], os.Args[i+1:]...)
i--
default:
cfg.Args = append(cfg.Args, arg)
}
}
converter, err := parse.NewConverter(cfg) converter, err := parse.NewConverter(cfg)
check(err) check(err)
_, err = converter.Convert() _, err = converter.Convert()
check(err) check(err)
result := converter.MarshalOutputASTFiles() result := converter.MarshalOutputASTFiles()
cstr := result.Print() cstr := result.Print()
outputResult(cstr, outputToFile) outputResult(cstr, outToFile)
cjson.FreeCStr(cstr) cjson.FreeCStr(cstr)
result.Delete() result.Delete()
converter.Dispose() converter.Dispose()
@@ -171,20 +202,52 @@ func outputResult(result *c.Char, outputToFile bool) {
fmt.Fprintf(os.Stderr, "Error writing to output file: %v\n", err) fmt.Fprintf(os.Stderr, "Error writing to output file: %v\n", err)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("Results saved to %s\n", outputFile) fmt.Fprintf(os.Stderr, "Results saved to %s\n", outputFile)
} else { } else {
c.Printf(result) c.Printf(result)
} }
} }
func getHeaderFiles(cflags string, files []string) []string { // todo(zzy): reuse the llcppsymg's cflags parse https://github.com/goplus/llgo/pull/788
prefix := cflags type CFlags struct {
prefix = strings.TrimPrefix(prefix, "-I") Paths []string // Include Path
var paths []string }
for _, f := range files {
paths = append(paths, filepath.Join(prefix, f)) func ParseCFlags(cflags string) *CFlags {
parts := strings.Fields(cflags)
cf := &CFlags{}
for _, part := range parts {
if strings.HasPrefix(part, "-I") {
cf.Paths = append(cf.Paths, part[2:])
}
} }
return paths return cf
}
func (cf *CFlags) GenHeaderFilePaths(files []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
for _, file := range files {
var found bool
for _, path := range cf.Paths {
fullPath := filepath.Join(path, file)
if _, err := os.Stat(fullPath); err == nil {
foundPaths = append(foundPaths, fullPath)
found = true
break
}
}
if !found {
notFound = append(notFound, file)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any header files")
}
return foundPaths, notFound, nil
} }
func outputInfo(context *parse.Context, outputToFile bool) { func outputInfo(context *parse.Context, outputToFile bool) {
@@ -198,12 +261,12 @@ func outputInfo(context *parse.Context, outputToFile bool) {
func parseBoolArg(arg, name string, defaultValue bool) bool { func parseBoolArg(arg, name string, defaultValue bool) bool {
parts := strings.SplitN(arg, "=", 2) parts := strings.SplitN(arg, "=", 2)
if len(parts) != 2 { if len(parts) != 2 {
fmt.Printf("Warning: Invalid -%s= argument, defaulting to %v\n", name, defaultValue) fmt.Fprintf(os.Stderr, "Warning: Invalid -%s= argument, defaulting to %v\n", name, defaultValue)
return defaultValue return defaultValue
} }
value, err := strconv.ParseBool(parts[1]) value, err := strconv.ParseBool(parts[1])
if err != nil { if err != nil {
fmt.Printf("Warning: Invalid -%s= value '%s', defaulting to %v\n", name, parts[1], defaultValue) fmt.Fprintf(os.Stderr, "Warning: Invalid -%s= value '%s', defaulting to %v\n", name, parts[1], defaultValue)
return defaultValue return defaultValue
} }
return value return value

View File

@@ -3,6 +3,7 @@ package parse
import ( import (
"fmt" "fmt"
"os" "os"
"runtime"
"strings" "strings"
"unsafe" "unsafe"
@@ -47,6 +48,7 @@ type Converter struct {
// Example: // Example:
// typedef struct { int x; } MyStruct; // typedef struct { int x; } MyStruct;
anonyTypeMap map[string]bool // cursorUsr anonyTypeMap map[string]bool // cursorUsr
indent int // for verbose debug
} }
var tagMap = map[string]ast.Tag{ var tagMap = map[string]ast.Tag{
@@ -64,6 +66,14 @@ type Config struct {
} }
func NewConverter(config *clangutils.Config) (*Converter, error) { func NewConverter(config *clangutils.Config) (*Converter, error) {
if debugParse {
fmt.Fprintln(os.Stderr, "NewConverter: config")
fmt.Fprintln(os.Stderr, "config.File", config.File)
fmt.Fprintln(os.Stderr, "config.Args", config.Args)
fmt.Fprintln(os.Stderr, "config.IsCpp", config.IsCpp)
fmt.Fprintln(os.Stderr, "config.Temp", config.Temp)
}
index, unit, err := clangutils.CreateTranslationUnit(config) index, unit, err := clangutils.CreateTranslationUnit(config)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -79,6 +89,7 @@ func NewConverter(config *clangutils.Config) (*Converter, error) {
} }
func (ct *Converter) Dispose() { func (ct *Converter) Dispose() {
ct.logln("Dispose")
ct.index.Dispose() ct.index.Dispose()
ct.unit.Dispose() ct.unit.Dispose()
} }
@@ -104,65 +115,87 @@ func (ct *Converter) GetTokens(cursor clang.Cursor) []*ast.Token {
return result return result
} }
func (ct *Converter) logBase() string {
return strings.Repeat(" ", ct.indent)
}
func (ct *Converter) incIndent() {
ct.indent++
}
func (ct *Converter) decIndent() {
if ct.indent > 0 {
ct.indent--
}
}
func (ct *Converter) logf(format string, args ...interface{}) {
if debugParse {
fmt.Fprintf(os.Stderr, ct.logBase()+format, args...)
}
}
func (ct *Converter) logln(args ...interface{}) {
if debugParse {
if len(args) > 0 {
firstArg := fmt.Sprintf("%s%v", ct.logBase(), args[0])
fmt.Fprintln(os.Stderr, append([]interface{}{firstArg}, args[1:]...)...)
} else {
fmt.Fprintln(os.Stderr, ct.logBase())
}
}
}
func (ct *Converter) UpdateLoc(cursor clang.Cursor) { func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
loc := cursor.Location() loc := cursor.Location()
var file clang.File var file clang.File
loc.SpellingLocation(&file, nil, nil, nil) loc.SpellingLocation(&file, nil, nil, nil)
filename := file.FileName()
defer filename.Dispose()
if filename.CStr() == nil { filePath := toStr(file.FileName())
if filePath == "" {
//todo(zzy): For some built-in macros, there is no file. //todo(zzy): For some built-in macros, there is no file.
ct.curLoc = ast.Location{File: ""} ct.curLoc = ast.Location{File: ""}
return return
} }
filePath := c.GoString(filename.CStr())
ct.curLoc = ast.Location{File: filePath} ct.curLoc = ast.Location{File: filePath}
} }
func (ct *Converter) GetCurFile() *ast.File { func (ct *Converter) GetCurFile() *ast.File {
if ct.curLoc.File == "" { if ct.curLoc.File == "" {
ct.logln("GetCurFile: NO FILE")
return nil return nil
} }
// todo(zzy): more efficient // todo(zzy): more efficient
for i, entry := range ct.Files { for i, entry := range ct.Files {
if entry.Path == ct.curLoc.File { if entry.Path == ct.curLoc.File {
ct.logln("GetCurFile: found", ct.curLoc.File)
return ct.Files[i].Doc return ct.Files[i].Doc
} }
} }
ct.logln("GetCurFile: Create New ast.File", ct.curLoc.File)
newDoc := &ast.File{} newDoc := &ast.File{}
ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc}) ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc})
return newDoc return newDoc
} }
func (ct *Converter) SetAnonyType(cursor clang.Cursor) { func (ct *Converter) SetAnonyType(cursor clang.Cursor) {
usr := cursor.USR() usr := toStr(cursor.USR())
usrStr := c.GoString(usr.CStr()) ct.anonyTypeMap[usr] = true
defer usr.Dispose()
ct.anonyTypeMap[usrStr] = true
} }
func (ct *Converter) GetAnonyType(cursor clang.Cursor) (bool, bool) { func (ct *Converter) GetAnonyType(cursor clang.Cursor) (bool, bool) {
usr := cursor.USR() usr := toStr(cursor.USR())
usrStr := c.GoString(usr.CStr()) isAnony, ok := ct.anonyTypeMap[usr]
defer usr.Dispose()
isAnony, ok := ct.anonyTypeMap[usrStr]
return isAnony, ok return isAnony, ok
} }
func (ct *Converter) SetTypeDecl(cursor clang.Cursor, decl ast.Decl) { func (ct *Converter) SetTypeDecl(cursor clang.Cursor, decl ast.Decl) {
usr := cursor.USR() usr := toStr(cursor.USR())
usrStr := c.GoString(usr.CStr()) ct.typeDecls[usr] = decl
ct.typeDecls[usrStr] = decl
usr.Dispose()
} }
func (ct *Converter) GetTypeDecl(cursor clang.Cursor) (ast.Decl, bool) { func (ct *Converter) GetTypeDecl(cursor clang.Cursor) (ast.Decl, bool) {
usr := cursor.USR() usr := toStr(cursor.USR())
usrStr := c.GoString(usr.CStr()) decl, ok := ct.typeDecls[usr]
decl, ok := ct.typeDecls[usrStr]
usr.Dispose()
return decl, ok return decl, ok
} }
@@ -190,14 +223,13 @@ func (ct *Converter) CreateDeclBase(cursor clang.Cursor) ast.DeclBase {
// Note: In cases where both documentation comments and line comments conceptually exist, // Note: In cases where both documentation comments and line comments conceptually exist,
// only the line comment will be preserved. // only the line comment will be preserved.
func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (comentGroup *ast.CommentGroup, isDoc bool) { func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (comentGroup *ast.CommentGroup, isDoc bool) {
rawComment := cursor.RawCommentText() rawComment := toStr(cursor.RawCommentText())
defer rawComment.Dispose()
commentGroup := &ast.CommentGroup{} commentGroup := &ast.CommentGroup{}
if rawComment.CStr() != nil { if rawComment != "" {
commentRange := cursor.CommentRange() commentRange := cursor.CommentRange()
cursorRange := cursor.Extent() cursorRange := cursor.Extent()
isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart()) isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart())
commentGroup = ct.ParseComment(c.GoString(rawComment.CStr())) commentGroup = ct.ParseComment(rawComment)
if len(commentGroup.List) > 0 { if len(commentGroup.List) > 0 {
return commentGroup, isDoc return commentGroup, isDoc
} }
@@ -216,9 +248,16 @@ func (ct *Converter) ParseComment(rawComment string) *ast.CommentGroup {
// visit top decls (struct,class,function,enum & macro,include) // visit top decls (struct,class,function,enum & macro,include)
func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResult { func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResult {
ct.incIndent()
defer ct.decIndent()
ct.UpdateLoc(cursor) ct.UpdateLoc(cursor)
curFile := ct.GetCurFile() curFile := ct.GetCurFile()
name := toStr(cursor.String())
ct.logf("visitTop: Cursor: %s\n", name)
if curFile == nil { if curFile == nil {
return clang.ChildVisit_Continue return clang.ChildVisit_Continue
} }
@@ -227,27 +266,57 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul
case clang.CursorInclusionDirective: case clang.CursorInclusionDirective:
include := ct.ProcessInclude(cursor) include := ct.ProcessInclude(cursor)
curFile.Includes = append(curFile.Includes, include) curFile.Includes = append(curFile.Includes, include)
ct.logln("visitTop: ProcessInclude END ", include.Path)
case clang.CursorMacroDefinition: case clang.CursorMacroDefinition:
macro := ct.ProcessMacro(cursor) macro := ct.ProcessMacro(cursor)
curFile.Macros = append(curFile.Macros, macro) curFile.Macros = append(curFile.Macros, macro)
ct.logln("visitTop: ProcessMacro END ", macro.Name, "Tokens Length:", len(macro.Tokens))
case clang.CursorEnumDecl: case clang.CursorEnumDecl:
enum := ct.ProcessEnumDecl(cursor) enum := ct.ProcessEnumDecl(cursor)
curFile.Decls = append(curFile.Decls, enum) curFile.Decls = append(curFile.Decls, enum)
ct.logf("visitTop: ProcessEnumDecl END")
if enum.Name != nil {
ct.logln(enum.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorClassDecl: case clang.CursorClassDecl:
classDecl := ct.ProcessClassDecl(cursor) classDecl := ct.ProcessClassDecl(cursor)
curFile.Decls = append(curFile.Decls, classDecl) curFile.Decls = append(curFile.Decls, classDecl)
// class havent anonymous situation
ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name)
case clang.CursorStructDecl: case clang.CursorStructDecl:
structDecl := ct.ProcessStructDecl(cursor) structDecl := ct.ProcessStructDecl(cursor)
curFile.Decls = append(curFile.Decls, structDecl) curFile.Decls = append(curFile.Decls, structDecl)
ct.logf("visitTop: ProcessStructDecl END")
if structDecl.Name != nil {
ct.logln(structDecl.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorUnionDecl: case clang.CursorUnionDecl:
unionDecl := ct.ProcessUnionDecl(cursor) unionDecl := ct.ProcessUnionDecl(cursor)
curFile.Decls = append(curFile.Decls, unionDecl) curFile.Decls = append(curFile.Decls, unionDecl)
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: case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
// Handle functions and class methods (including out-of-class method) // Handle functions and class methods (including out-of-class method)
// Example: void MyClass::myMethod() { ... } out-of-class method // Example: void MyClass::myMethod() { ... } out-of-class method
curFile.Decls = append(curFile.Decls, ct.ProcessFuncDecl(cursor)) funcDecl := ct.ProcessFuncDecl(cursor)
curFile.Decls = append(curFile.Decls, funcDecl)
ct.logln("visitTop: ProcessFuncDecl END", funcDecl.Name.Name, funcDecl.MangledName, "isStatic:", funcDecl.IsStatic, "isInline:", funcDecl.IsInline)
case clang.CursorTypedefDecl: case clang.CursorTypedefDecl:
curFile.Decls = append(curFile.Decls, ct.ProcessTypeDefDecl(cursor)) typedefDecl := ct.ProcessTypeDefDecl(cursor)
curFile.Decls = append(curFile.Decls, typedefDecl)
ct.logln("visitTop: ProcessTypeDefDecl END", typedefDecl.Name.Name)
case clang.CursorNamespace: case clang.CursorNamespace:
VisitChildren(cursor, ct.visitTop) VisitChildren(cursor, ct.visitTop)
} }
@@ -271,6 +340,12 @@ func VisitChildren(cursor clang.Cursor, fn Visitor) c.Uint {
} }
func (ct *Converter) ProcessType(t clang.Type) ast.Expr { func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessType: TypeName:", typeName, "TypeKind:", typeKind)
if t.Kind >= clang.TypeFirstBuiltin && t.Kind <= clang.TypeLastBuiltin { if t.Kind >= clang.TypeFirstBuiltin && t.Kind <= clang.TypeLastBuiltin {
return ct.ProcessBuiltinType(t) return ct.ProcessBuiltinType(t)
} }
@@ -279,17 +354,29 @@ func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
return ct.ProcessElaboratedType(t) return ct.ProcessElaboratedType(t)
} }
if t.Kind == clang.TypeTypedef {
return ct.ProcessTypeDefType(t)
}
var expr ast.Expr var expr ast.Expr
switch t.Kind { switch t.Kind {
case clang.TypePointer: case clang.TypePointer:
name, kind := getTypeDesc(t.PointeeType())
ct.logln("ProcessType: PointerType Pointee TypeName:", name, "TypeKind:", kind)
expr = &ast.PointerType{X: ct.ProcessType(t.PointeeType())} expr = &ast.PointerType{X: ct.ProcessType(t.PointeeType())}
case clang.TypeLValueReference: case clang.TypeLValueReference:
name, kind := getTypeDesc(t.NonReferenceType())
ct.logln("ProcessType: LvalueRefType NonReference TypeName:", name, "TypeKind:", kind)
expr = &ast.LvalueRefType{X: ct.ProcessType(t.NonReferenceType())} expr = &ast.LvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
case clang.TypeRValueReference: case clang.TypeRValueReference:
name, kind := getTypeDesc(t.NonReferenceType())
ct.logln("ProcessType: RvalueRefType NonReference TypeName:", name, "TypeKind:", kind)
expr = &ast.RvalueRefType{X: ct.ProcessType(t.NonReferenceType())} expr = &ast.RvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
case clang.TypeFunctionProto, clang.TypeFunctionNoProto: case clang.TypeFunctionProto, clang.TypeFunctionNoProto:
// treating TypeFunctionNoProto as a general function without parameters // treating TypeFunctionNoProto as a general function without parameters
// function type will only collect return type, params will be collected in ProcessFuncDecl // function type will only collect return type, params will be collected in ProcessFuncDecl
name, kind := getTypeDesc(t)
ct.logln("ProcessType: FunctionType TypeName:", name, "TypeKind:", kind)
expr = ct.ProcessFunctionType(t) expr = ct.ProcessFunctionType(t)
case clang.TypeConstantArray, clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray: case clang.TypeConstantArray, clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray:
if t.Kind == clang.TypeConstantArray { if t.Kind == clang.TypeConstantArray {
@@ -306,6 +393,9 @@ func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
Elt: ct.ProcessType(t.ArrayElementType()), Elt: ct.ProcessType(t.ArrayElementType()),
} }
} }
default:
name, kind := getTypeDesc(t)
ct.logln("ProcessType: Unknown Type TypeName:", name, "TypeKind:", kind)
} }
return expr return expr
} }
@@ -314,11 +404,19 @@ func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
// This is because we cannot reverse-lookup the corresponding declaration node from a function type. // This is because we cannot reverse-lookup the corresponding declaration node from a function type.
// Note: For function declarations, parameter names are collected in the ProcessFuncDecl method. // Note: For function declarations, parameter names are collected in the ProcessFuncDecl method.
func (ct *Converter) ProcessFunctionType(t clang.Type) *ast.FuncType { func (ct *Converter) ProcessFunctionType(t clang.Type) *ast.FuncType {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessFunctionType: TypeName:", typeName, "TypeKind:", typeKind)
// Note: Attempting to get the type declaration for a function type will result in CursorNoDeclFound // Note: Attempting to get the type declaration for a function type will result in CursorNoDeclFound
// cursor := t.TypeDeclaration() // cursor := t.TypeDeclaration()
// This would return CursorNoDeclFound // This would return CursorNoDeclFound
resType := t.ResultType()
ret := ct.ProcessType(t.ResultType()) name, kind := getTypeDesc(resType)
ct.logln("ProcessFunctionType: ResultType TypeName:", name, "TypeKind:", kind)
ret := ct.ProcessType(resType)
params := &ast.FieldList{} params := &ast.FieldList{}
numArgs := t.NumArgTypes() numArgs := t.NumArgTypes()
for i := 0; i < int(numArgs); i++ { for i := 0; i < int(numArgs); i++ {
@@ -340,24 +438,29 @@ func (ct *Converter) ProcessFunctionType(t clang.Type) *ast.FuncType {
} }
func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl { func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl {
name := cursor.String() ct.incIndent()
defer name.Dispose() defer ct.decIndent()
name, kind := getCursorDesc(cursor)
ct.logln("ProcessTypeDefDecl: CursorName:", name, "CursorKind:", kind, "CursorTypeKind:", toStr(cursor.Type().Kind.String()))
typ := ct.ProcessUnderlyingType(cursor) typ := ct.ProcessUnderlyingType(cursor)
decl := &ast.TypedefDecl{ decl := &ast.TypedefDecl{
DeclBase: ct.CreateDeclBase(cursor), DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: c.GoString(name.CStr())}, Name: &ast.Ident{Name: name},
Type: typ, Type: typ,
} }
ct.SetTypeDecl(cursor, decl) ct.SetTypeDecl(cursor, decl)
return decl return decl
} }
func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr { func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
underlyingTyp := cursor.TypedefDeclUnderlyingType() underlyingTyp := cursor.TypedefDeclUnderlyingType()
if underlyingTyp.Kind != clang.TypeElaborated { if underlyingTyp.Kind != clang.TypeElaborated {
ct.logln("ProcessUnderlyingType: not elaborated")
return ct.ProcessType(underlyingTyp) return ct.ProcessType(underlyingTyp)
} }
@@ -370,6 +473,7 @@ func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
// In this case, libclang incorrectly reports an anonymous struct as a named struct // In this case, libclang incorrectly reports an anonymous struct as a named struct
sourceCode := ct.GetTokens(referTypeCursor) sourceCode := ct.GetTokens(referTypeCursor)
if isAnonymousStructure(sourceCode) { if isAnonymousStructure(sourceCode) {
ct.logln("ProcessUnderlyingType: is anonymous structure")
ct.SetAnonyType(referTypeCursor) ct.SetAnonyType(referTypeCursor)
typ, isValidType := ct.GetTypeDecl(referTypeCursor) typ, isValidType := ct.GetTypeDecl(referTypeCursor)
if isValidType { if isValidType {
@@ -377,9 +481,11 @@ func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
// according to a normal anonymous decl // according to a normal anonymous decl
switch declType := typ.(type) { switch declType := typ.(type) {
case *ast.EnumTypeDecl: case *ast.EnumTypeDecl:
ct.logln("ProcessUnderlyingType: is actually anonymous enum,remove name")
declType.Name = nil declType.Name = nil
case *ast.TypeDecl: case *ast.TypeDecl:
if declType.Type.Tag != ast.Class { if declType.Type.Tag != ast.Class {
ct.logln("ProcessUnderlyingType: is actually anonymous struct,remove name")
declType.Name = nil declType.Name = nil
} else { } else {
// Unreachable: There should be no anonymous classes in this context // Unreachable: There should be no anonymous classes in this context
@@ -398,31 +504,33 @@ func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
// converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes. // converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes.
func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl { func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
name := cursor.String() ct.incIndent()
mangledName := cursor.Mangling() defer ct.decIndent()
defer name.Dispose() name, kind := getCursorDesc(cursor)
defer mangledName.Dispose() mangledName := toStr(cursor.Mangling())
ct.logln("ProcessFuncDecl: CursorName:", name, "CursorKind:", kind)
// function type will only collect return type // function type will only collect return type
// ProcessType can't get the field names,will collect in follows // ProcessType can't get the field names,will collect in follows
funcType, ok := ct.ProcessType(cursor.Type()).(*ast.FuncType) funcType, ok := ct.ProcessType(cursor.Type()).(*ast.FuncType)
if !ok { if !ok {
fmt.Println("failed to process function type") ct.logln("ProcessFuncDecl: failed to process function type")
return nil return nil
} }
ct.logln("ProcessFuncDecl: ProcessFieldList")
params := ct.ProcessFieldList(cursor) params := ct.ProcessFieldList(cursor)
funcType.Params = params funcType.Params = params
mangledNameStr := c.GoString(mangledName.CStr()) // Linux has one less leading underscore than macOS, so remove one leading underscore on macOS
if len(mangledNameStr) >= 1 && mangledNameStr[0] == '_' { if runtime.GOOS == "darwin" {
mangledNameStr = mangledNameStr[1:] mangledName = strings.TrimPrefix(mangledName, "_")
} }
funcDecl := &ast.FuncDecl{ funcDecl := &ast.FuncDecl{
DeclBase: ct.CreateDeclBase(cursor), DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: c.GoString(name.CStr())}, Name: &ast.Ident{Name: name},
Type: funcType, Type: funcType,
MangledName: mangledNameStr, MangledName: mangledName,
} }
if cursor.IsFunctionInlined() != 0 { if cursor.IsFunctionInlined() != 0 {
@@ -430,6 +538,7 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
} }
if isMethod(cursor) { if isMethod(cursor) {
ct.logln("ProcessFuncDecl: is method, ProcessMethodAttributes")
ct.ProcessMethodAttributes(cursor, funcDecl) ct.ProcessMethodAttributes(cursor, funcDecl)
} else { } else {
if cursor.StorageClass() == clang.SCStatic { if cursor.StorageClass() == clang.SCStatic {
@@ -438,7 +547,6 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
} }
ct.SetTypeDecl(cursor, funcDecl) ct.SetTypeDecl(cursor, funcDecl)
return funcDecl return funcDecl
} }
@@ -507,51 +615,58 @@ func (ct *Converter) ProcessEnumType(cursor clang.Cursor) *ast.EnumType {
} }
func (ct *Converter) ProcessEnumDecl(cursor clang.Cursor) *ast.EnumTypeDecl { func (ct *Converter) ProcessEnumDecl(cursor clang.Cursor) *ast.EnumTypeDecl {
name := cursor.String() cursorName, cursorKind := getCursorDesc(cursor)
defer name.Dispose() ct.logln("ProcessEnumDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
decl := &ast.EnumTypeDecl{ decl := &ast.EnumTypeDecl{
DeclBase: ct.CreateDeclBase(cursor), DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: c.GoString(name.CStr())},
Type: ct.ProcessEnumType(cursor), Type: ct.ProcessEnumType(cursor),
} }
anony := cursor.IsAnonymous()
if anony == 0 {
decl.Name = &ast.Ident{Name: cursorName}
ct.logln("ProcessEnumDecl: has name", cursorName)
} else {
ct.logln("ProcessRecordDecl: is anonymous")
}
ct.SetTypeDecl(cursor, decl) ct.SetTypeDecl(cursor, decl)
return decl return decl
} }
// current only collect macro which defined in file // current only collect macro which defined in file
func (ct *Converter) ProcessMacro(cursor clang.Cursor) *ast.Macro { func (ct *Converter) ProcessMacro(cursor clang.Cursor) *ast.Macro {
name := cursor.String() name := toStr(cursor.String())
defer name.Dispose()
macro := &ast.Macro{ macro := &ast.Macro{
Name: c.GoString(name.CStr()), Name: name,
Tokens: ct.GetTokens(cursor), Tokens: ct.GetTokens(cursor),
} }
return macro return macro
} }
func (ct *Converter) ProcessInclude(cursor clang.Cursor) *ast.Include { func (ct *Converter) ProcessInclude(cursor clang.Cursor) *ast.Include {
name := cursor.String() name := toStr(cursor.String())
defer name.Dispose() return &ast.Include{Path: name}
return &ast.Include{Path: c.GoString(name.CStr())}
} }
// todo(zzy): after https://github.com/goplus/llgo/issues/804 has be resolved func (ct *Converter) createBaseField(cursor clang.Cursor) *ast.Field {
// Change the following code to use the closure ct.incIndent()
type visitFieldContext struct { defer ct.decIndent()
params *ast.FieldList
converter *Converter fieldName := toStr(cursor.String())
}
typ := cursor.Type()
typeName, typeKind := getTypeDesc(typ)
ct.logf("createBaseField: ProcessType %s TypeKind: %s", typeName, typeKind)
func (p *visitFieldContext) createBaseField(cursor clang.Cursor) *ast.Field {
field := &ast.Field{ field := &ast.Field{
Type: p.converter.ProcessType(cursor.Type()), Type: ct.ProcessType(typ),
} }
fieldName := cursor.String()
defer fieldName.Dispose()
commentGroup, isDoc := p.converter.ParseCommentGroup(cursor) commentGroup, isDoc := ct.ParseCommentGroup(cursor)
if commentGroup != nil { if commentGroup != nil {
if isDoc { if isDoc {
field.Doc = commentGroup field.Doc = commentGroup
@@ -559,51 +674,54 @@ func (p *visitFieldContext) createBaseField(cursor clang.Cursor) *ast.Field {
field.Comment = commentGroup field.Comment = commentGroup
} }
} }
if name := fieldName.CStr(); name != nil { if fieldName != "" {
field.Names = []*ast.Ident{{Name: c.GoString(name)}} field.Names = []*ast.Ident{{Name: fieldName}}
} }
return field return field
} }
func visitFieldList(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
ctx := (*visitFieldContext)(clientData)
switch cursor.Kind {
case clang.CursorParmDecl, clang.CursorFieldDecl:
// In C language, parameter lists do not have similar parameter grouping in Go.
// func foo(a, b int)
// For follows struct, it will also parse to two FieldDecl
// struct A {
// int a, b;
// };
field := ctx.createBaseField(cursor)
if cursor.Kind == clang.CursorFieldDecl {
field.Access = ast.AccessSpecifier(cursor.CXXAccessSpecifier())
}
ctx.params.List = append(ctx.params.List, field)
case clang.CursorVarDecl:
if cursor.StorageClass() == clang.SCStatic {
// static member variable
field := ctx.createBaseField(cursor)
field.Access = ast.AccessSpecifier(cursor.CXXAccessSpecifier())
field.IsStatic = true
ctx.params.List = append(ctx.params.List, field)
}
}
return clang.ChildVisit_Continue
}
// For Record Type(struct,union ...) & Func 's FieldList // For Record Type(struct,union ...) & Func 's FieldList
func (ct *Converter) ProcessFieldList(cursor clang.Cursor) *ast.FieldList { func (ct *Converter) ProcessFieldList(cursor clang.Cursor) *ast.FieldList {
ct.incIndent()
defer ct.decIndent()
params := &ast.FieldList{} params := &ast.FieldList{}
ctx := &visitFieldContext{ ct.logln("ProcessFieldList: VisitChildren")
params: params, VisitChildren(cursor, func(subcsr, parent clang.Cursor) clang.ChildVisitResult {
converter: ct, switch subcsr.Kind {
} case clang.CursorParmDecl, clang.CursorFieldDecl:
clang.VisitChildren(cursor, visitFieldList, c.Pointer(ctx)) // In C language, parameter lists do not have similar parameter grouping in Go.
// func foo(a, b int)
// For follows struct, it will also parse to two FieldDecl
// struct A {
// int a, b;
// };
if subcsr.Kind == clang.CursorFieldDecl {
ct.logln("ProcessFieldList: CursorFieldDecl")
} else {
ct.logln("ProcessFieldList: CursorParmDecl")
}
field := ct.createBaseField(subcsr)
if subcsr.Kind == clang.CursorFieldDecl {
field.Access = ast.AccessSpecifier(subcsr.CXXAccessSpecifier())
}
params.List = append(params.List, field)
case clang.CursorVarDecl:
if subcsr.StorageClass() == clang.SCStatic {
// static member variable
field := ct.createBaseField(subcsr)
field.Access = ast.AccessSpecifier(subcsr.CXXAccessSpecifier())
field.IsStatic = true
params.List = append(params.List, field)
}
}
return clang.ChildVisit_Continue
})
if (cursor.Kind == clang.CursorFunctionDecl || isMethod(cursor)) && cursor.IsVariadic() != 0 { if (cursor.Kind == clang.CursorFunctionDecl || isMethod(cursor)) && cursor.IsVariadic() != 0 {
params.List = append(params.List, &ast.Field{ params.List = append(params.List, &ast.Field{
Type: &ast.Variadic{}, Type: &ast.Variadic{},
@@ -612,49 +730,41 @@ func (ct *Converter) ProcessFieldList(cursor clang.Cursor) *ast.FieldList {
return params return params
} }
type visitMethodsContext struct {
methods *[]*ast.FuncDecl
converter *Converter
}
func visitMethods(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
ctx := (*visitMethodsContext)(clientData)
if isMethod(cursor) && cursor.CXXAccessSpecifier() == clang.CXXPublic {
method := ctx.converter.ProcessFuncDecl(cursor)
if method != nil {
*ctx.methods = append(*ctx.methods, method)
}
}
return clang.ChildVisit_Continue
}
// Note:Public Method is considered // Note:Public Method is considered
func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl { func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
methods := make([]*ast.FuncDecl, 0) methods := make([]*ast.FuncDecl, 0)
ctx := &visitMethodsContext{ VisitChildren(cursor, func(subcsr, parent clang.Cursor) clang.ChildVisitResult {
methods: &methods, if isMethod(subcsr) && subcsr.CXXAccessSpecifier() == clang.CXXPublic {
converter: ct, method := ct.ProcessFuncDecl(subcsr)
} if method != nil {
clang.VisitChildren(cursor, visitMethods, c.Pointer(ctx)) methods = append(methods, method)
}
}
return clang.ChildVisit_Continue
})
return methods return methods
} }
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl { func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
anony := cursor.IsAnonymousRecordDecl() ct.incIndent()
var name *ast.Ident defer ct.decIndent()
if anony == 0 { cursorName, cursorKind := getCursorDesc(cursor)
cursorName := cursor.String() ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
defer cursorName.Dispose()
name = &ast.Ident{Name: c.GoString(cursorName.CStr())}
}
decl := &ast.TypeDecl{ decl := &ast.TypeDecl{
DeclBase: ct.CreateDeclBase(cursor), DeclBase: ct.CreateDeclBase(cursor),
Name: name,
Type: ct.ProcessRecordType(cursor), Type: ct.ProcessRecordType(cursor),
} }
ct.SetTypeDecl(cursor, decl)
anony := cursor.IsAnonymousRecordDecl()
if anony == 0 {
decl.Name = &ast.Ident{Name: cursorName}
ct.logln("ProcessRecordDecl: has name", cursorName)
} else {
ct.logln("ProcessRecordDecl: is anonymous")
}
ct.SetTypeDecl(cursor, decl)
return decl return decl
} }
@@ -667,25 +777,43 @@ func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl {
} }
func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl { func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl {
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessClassDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
// Pushing class scope before processing its type and popping after // Pushing class scope before processing its type and popping after
base := ct.CreateDeclBase(cursor) base := ct.CreateDeclBase(cursor)
typ := ct.ProcessRecordType(cursor) typ := ct.ProcessRecordType(cursor)
decl := &ast.TypeDecl{ decl := &ast.TypeDecl{
DeclBase: base, DeclBase: base,
Name: &ast.Ident{Name: c.GoString(cursor.String().CStr())}, Name: &ast.Ident{Name: cursorName},
Type: typ, Type: typ,
} }
ct.SetTypeDecl(cursor, decl)
ct.SetTypeDecl(cursor, decl)
return decl return decl
} }
func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType { func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
ct.incIndent()
defer ct.decIndent()
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessRecordType: CursorName:", cursorName, "CursorKind:", cursorKind)
tag := toTag(cursor.Kind)
ct.logln("ProcessRecordType: toTag", tag)
ct.logln("ProcessRecordType: ProcessFieldList")
fields := ct.ProcessFieldList(cursor)
ct.logln("ProcessRecordType: ProcessMethods")
methods := ct.ProcessMethods(cursor)
return &ast.RecordType{ return &ast.RecordType{
Tag: toTag(cursor.Kind), Tag: tag,
Fields: ct.ProcessFieldList(cursor), Fields: fields,
Methods: ct.ProcessMethods(cursor), Methods: methods,
} }
} }
@@ -699,8 +827,10 @@ func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
// - Examples: struct { int x; int y; }, union { int a; float b; } // - Examples: struct { int x; int y; }, union { int a; float b; }
// - Handling: Retrieve their corresponding concrete types // - Handling: Retrieve their corresponding concrete types
func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr { func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr {
name := t.String() ct.incIndent()
defer name.Dispose() defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessElaboratedType: TypeName:", typeName, "TypeKind:", typeKind)
decl := t.TypeDeclaration() decl := t.TypeDeclaration()
isAnony, ok := ct.GetAnonyType(decl) isAnony, ok := ct.GetAnonyType(decl)
@@ -713,8 +843,6 @@ func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr {
return ct.ProcessRecordType(decl) return ct.ProcessRecordType(decl)
} }
typeName := c.GoString(name.CStr())
// for elaborated type, it could have a tag description // for elaborated type, it could have a tag description
// like struct A, union B, class C, enum D // like struct A, union B, class C, enum D
parts := strings.SplitN(typeName, " ", 2) parts := strings.SplitN(typeName, " ", 2)
@@ -730,7 +858,22 @@ func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr {
return ct.BuildScopingExpr(decl) return ct.BuildScopingExpr(decl)
} }
func (ct *Converter) ProcessTypeDefType(t clang.Type) ast.Expr {
cursor := t.TypeDeclaration()
ct.logln("ProcessTypeDefType: Typedef TypeDeclaration", toStr(cursor.String()), toStr(t.String()))
if name := toStr(cursor.String()); name != "" {
return &ast.Ident{Name: name}
}
ct.logln("ProcessTypeDefType: typedef type have no name")
return nil
}
func (ct *Converter) ProcessBuiltinType(t clang.Type) *ast.BuiltinType { func (ct *Converter) ProcessBuiltinType(t clang.Type) *ast.BuiltinType {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessBuiltinType: TypeName:", typeName, "TypeKind:", typeKind)
kind := ast.Void kind := ast.Void
var flags ast.TypeFlag var flags ast.TypeFlag
@@ -783,9 +926,8 @@ func (ct *Converter) ProcessBuiltinType(t clang.Type) *ast.BuiltinType {
// float complfex flag is not set // float complfex flag is not set
default: default:
// like IBM128,NullPtr,Accum // like IBM128,NullPtr,Accum
kindStr := t.Kind.String() kindStr := toStr(t.Kind.String())
defer kindStr.Dispose() fmt.Fprintln(os.Stderr, "todo: unknown builtin type:", kindStr)
fmt.Fprintln(os.Stderr, "todo: unknown builtin type:", c.GoString(kindStr.CStr()))
} }
if IsExplicitSigned(t) { if IsExplicitSigned(t) {
@@ -892,3 +1034,23 @@ func isAnonymousStructure(sourceCode []*ast.Token) bool {
sourceCode[1].Token == token.PUNCT && sourceCode[1].Token == token.PUNCT &&
sourceCode[1].Lit == "{" sourceCode[1].Lit == "{"
} }
func toStr(clangStr clang.String) (str string) {
defer clangStr.Dispose()
if clangStr.CStr() != nil {
str = c.GoString(clangStr.CStr())
}
return
}
func getTypeDesc(t clang.Type) (name string, kind string) {
name = toStr(t.String())
kind = toStr(t.Kind.String())
return
}
func getCursorDesc(cursor clang.Cursor) (name string, kind string) {
name = toStr(cursor.String())
kind = toStr(cursor.Kind.String())
return
}

View File

@@ -8,6 +8,11 @@ func main() {
func TestEnumDecl() { func TestEnumDecl() {
testCases := []string{ testCases := []string{
`enum {
a,
b,
c,
};`,
`enum Foo { `enum Foo {
a, a,
b, b,

View File

@@ -11,10 +11,7 @@ TestEnumDecl Case 1:
}, },
"Doc": null, "Doc": null,
"Parent": null, "Parent": null,
"Name": { "Name": null,
"_Type": "Ident",
"Name": "Foo"
},
"Type": { "Type": {
"_Type": "EnumType", "_Type": "EnumType",
"Items": [{ "Items": [{
@@ -59,6 +56,65 @@ TestEnumDecl Case 1:
} }
TestEnumDecl Case 2: TestEnumDecl Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "EnumTypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Type": {
"_Type": "EnumType",
"Items": [{
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "a"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "0"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "b"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "1"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "c"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "2"
}
}]
}
}],
"includes": [],
"macros": []
}
}
TestEnumDecl Case 3:
{ {
"temp.h": { "temp.h": {
"_Type": "File", "_Type": "File",
@@ -117,7 +173,7 @@ TestEnumDecl Case 2:
} }
} }
TestEnumDecl Case 3: TestEnumDecl Case 4:
{ {
"temp.h": { "temp.h": {
"_Type": "File", "_Type": "File",

View File

@@ -180,8 +180,8 @@ TestUnionDecl Case 3:
"_Type": "Field", "_Type": "Field",
"Type": { "Type": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
}, },
"Doc": null, "Doc": null,
"Comment": null, "Comment": null,

View File

@@ -20,7 +20,7 @@ func TestUnionDecl() {
int i; int i;
float f; float f;
union { union {
char c; int c;
short s; short s;
} inner; } inner;
};`, };`,

View File

@@ -1,4 +1,5 @@
#stdout #stdout
Char's flags is signed or unsigned
Void:flags:0 kind:0 Void:flags:0 kind:0
Bool:flags:0 kind:1 Bool:flags:0 kind:1
Char_S:flags:1 kind:2 Char_S:flags:1 kind:2
@@ -26,16 +27,16 @@ Complex:flags:0 kind:11
Complex:flags:16 kind:11 Complex:flags:16 kind:11
Complex:flags:20 kind:11 Complex:flags:20 kind:11
Unknown:flags:0 kind:0 Unknown:flags:0 kind:0
Type: char *: Type: int *:
{ {
"_Type": "PointerType", "_Type": "PointerType",
"X": { "X": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
} }
} }
Type: char ***: Type: int ***:
{ {
"_Type": "PointerType", "_Type": "PointerType",
"X": { "X": {
@@ -44,29 +45,29 @@ Type: char ***:
"_Type": "PointerType", "_Type": "PointerType",
"X": { "X": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
} }
} }
} }
} }
Type: char[]: Type: int[]:
{ {
"_Type": "ArrayType", "_Type": "ArrayType",
"Elt": { "Elt": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
}, },
"Len": null "Len": null
} }
Type: char[10]: Type: int[10]:
{ {
"_Type": "ArrayType", "_Type": "ArrayType",
"Elt": { "Elt": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
}, },
"Len": { "Len": {
"_Type": "BasicLit", "_Type": "BasicLit",
@@ -74,15 +75,15 @@ Type: char[10]:
"Value": "10" "Value": "10"
} }
} }
Type: char[3][4]: Type: int[3][4]:
{ {
"_Type": "ArrayType", "_Type": "ArrayType",
"Elt": { "Elt": {
"_Type": "ArrayType", "_Type": "ArrayType",
"Elt": { "Elt": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
}, },
"Len": { "Len": {
"_Type": "BasicLit", "_Type": "BasicLit",
@@ -303,7 +304,7 @@ Type: class a::b::c:
}, },
"Tag": 3 "Tag": 3
} }
Type: int (*)(int, char): Type: int (*)(int, int):
{ {
"_Type": "PointerType", "_Type": "PointerType",
"X": { "X": {
@@ -326,8 +327,8 @@ Type: int (*)(int, char):
"_Type": "Field", "_Type": "Field",
"Type": { "Type": {
"_Type": "BuiltinType", "_Type": "BuiltinType",
"Kind": 2, "Kind": 6,
"Flags": 1 "Flags": 0
}, },
"Doc": null, "Doc": null,
"Comment": null, "Comment": null,

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"os"
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/cjson"
@@ -12,6 +13,7 @@ import (
) )
func main() { func main() {
TestChar()
TestBuiltinType() TestBuiltinType()
TestNonBuiltinTypes() TestNonBuiltinTypes()
} }
@@ -64,14 +66,38 @@ func TestBuiltinType() {
} }
} }
// Char's Default Type in macos is signed char & in linux is unsigned char
// So we only confirm the char's kind is char & flags is unsigned or signed
func TestChar() {
typ, index, transunit := test.GetType(&test.GetTypeOptions{
TypeCode: "char",
IsCpp: false,
})
converter := &parse.Converter{}
expr := converter.ProcessType(typ)
if btType, ok := expr.(*ast.BuiltinType); ok {
if btType.Kind == ast.Char {
if btType.Flags == ast.Signed || btType.Flags == ast.Unsigned {
fmt.Println("Char's flags is signed or unsigned")
} else {
fmt.Fprintf(os.Stderr, "Char's flags is not signed or unsigned")
}
}
} else {
fmt.Fprintf(os.Stderr, "Char's expr is not a builtin type")
}
index.Dispose()
transunit.Dispose()
}
func TestNonBuiltinTypes() { func TestNonBuiltinTypes() {
tests := []string{ tests := []string{
"char*", "int*",
"char***", "int***",
"char[]", "int[]",
"char[10]", "int[10]",
"char[3][4]", "int[3][4]",
"int&", "int&",
"int&&", "int&&",
@@ -122,7 +148,7 @@ func TestNonBuiltinTypes() {
} }
class a::b::c`, class a::b::c`,
`int (*p)(int, char);`, `int (*p)(int, int);`,
} }
for _, t := range tests { for _, t := range tests {

View File

@@ -2,11 +2,28 @@ package parse
import ( import (
"errors" "errors"
"fmt"
"os"
"github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils" "github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
) )
type dbgFlags = int
const (
DbgParse dbgFlags = 1 << iota
DbgFlagAll = DbgParse
)
var (
debugParse bool
)
func SetDebug(dbgFlags dbgFlags) {
debugParse = (dbgFlags & DbgParse) != 0
}
type Context struct { type Context struct {
Files []*FileEntry Files []*FileEntry
IsCpp bool IsCpp bool
@@ -25,6 +42,9 @@ func (p *Context) Output() *cjson.JSON {
// ProcessFiles processes the given files and adds them to the context // ProcessFiles processes the given files and adds them to the context
func (p *Context) ProcessFiles(files []string) error { func (p *Context) ProcessFiles(files []string) error {
if debugParse {
fmt.Fprintln(os.Stderr, "ProcessFiles: files", files, "isCpp", p.IsCpp)
}
for _, file := range files { for _, file := range files {
if err := p.processFile(file); err != nil { if err := p.processFile(file); err != nil {
return err return err
@@ -35,8 +55,14 @@ func (p *Context) ProcessFiles(files []string) error {
// parse file and add it to the context,avoid duplicate parsing // parse file and add it to the context,avoid duplicate parsing
func (p *Context) processFile(path string) error { func (p *Context) processFile(path string) error {
if debugParse {
fmt.Fprintln(os.Stderr, "processFile: path", path)
}
for _, entry := range p.Files { for _, entry := range p.Files {
if entry.Path == path { if entry.Path == path {
if debugParse {
fmt.Fprintln(os.Stderr, "processFile: already parsed", path)
}
return nil return nil
} }
} }
@@ -50,6 +76,9 @@ func (p *Context) processFile(path string) error {
} }
func (p *Context) parseFile(path string) ([]*FileEntry, error) { func (p *Context) parseFile(path string) ([]*FileEntry, error) {
if debugParse {
fmt.Fprintln(os.Stderr, "parseFile: path", path)
}
converter, err := NewConverter(&clangutils.Config{ converter, err := NewConverter(&clangutils.Config{
File: path, File: path,
Temp: false, Temp: false,

View File

@@ -0,0 +1,63 @@
package main
import (
"fmt"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/args"
)
func main() {
TestParseArgs()
}
func TestParseArgs() {
fmt.Println("=== Test ParseArgs ===")
swflags := map[string]bool{
"--extract": true,
}
testCases := []struct {
name string
input []string
}{
{
name: "Basic flags",
input: []string{"-h", "-v", "-"},
},
{
name: "Config file",
input: []string{"lua.llcppg.cfg"},
},
{
name: "Extract with multiple args",
input: []string{"--extract", "file1.h", "file2.h", "-v"},
},
{
name: "Non-skippable flags",
input: []string{"--extract", "file1.h", "file2.h", "-out=true", "-cpp=true", "-v"},
},
{
name: "Mixed flags",
input: []string{"-v", "--extract", "file.h", "-out=true", "config.json"},
},
{
name: "Empty input",
input: []string{},
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input: %v\n", tc.input)
result, filteredArgs := args.ParseArgs(tc.input, swflags)
fmt.Printf("Help: %v\n", result.Help)
fmt.Printf("Verbose: %v\n", result.Verbose)
fmt.Printf("UseStdin: %v\n", result.UseStdin)
fmt.Printf("CfgFile: %s\n", result.CfgFile)
fmt.Printf("FilteredArgs: %v\n", filteredArgs)
fmt.Println()
}
}

View File

@@ -0,0 +1,54 @@
#stdout
=== Test ParseArgs ===
Test case: Basic flags
Input: [-h -v -]
Help: true
Verbose: true
UseStdin: true
CfgFile: llcppg.cfg
FilteredArgs: []
Test case: Config file
Input: [lua.llcppg.cfg]
Help: false
Verbose: false
UseStdin: false
CfgFile: lua.llcppg.cfg
FilteredArgs: []
Test case: Extract with multiple args
Input: [--extract file1.h file2.h -v]
Help: false
Verbose: true
UseStdin: false
CfgFile: llcppg.cfg
FilteredArgs: [--extract file1.h file2.h]
Test case: Non-skippable flags
Input: [--extract file1.h file2.h -out=true -cpp=true -v]
Help: false
Verbose: true
UseStdin: false
CfgFile: llcppg.cfg
FilteredArgs: [--extract file1.h file2.h -out=true -cpp=true]
Test case: Mixed flags
Input: [-v --extract file.h -out=true config.json]
Help: false
Verbose: true
UseStdin: false
CfgFile: config.json
FilteredArgs: [--extract file.h -out=true]
Test case: Empty input
Input: []
Help: false
Verbose: false
UseStdin: false
CfgFile: llcppg.cfg
FilteredArgs: []
#stderr
#exit 0

View File

@@ -0,0 +1,51 @@
package args
import "strings"
type Args struct {
Help bool
Verbose bool
UseStdin bool
CfgFile string
}
func ParseArgs(args []string, swflags map[string]bool) (*Args, []string) {
result := &Args{}
filteredArgs := []string{}
for i := 0; i < len(args); i++ {
arg := args[i]
if strings.HasPrefix(arg, "-") {
switch arg {
case "-h", "--help":
result.Help = true
continue
case "-v":
result.Verbose = true
continue
case "-":
result.UseStdin = true
continue
default:
if hasArg, ok := swflags[arg]; ok {
if hasArg {
filteredArgs = append(filteredArgs, arg)
for i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
filteredArgs = append(filteredArgs, args[i+1])
i++
}
continue
}
}
filteredArgs = append(filteredArgs, arg)
}
} else if result.CfgFile == "" {
result.CfgFile = arg
} else {
filteredArgs = append(filteredArgs, arg)
}
}
if result.CfgFile == "" {
result.CfgFile = "llcppg.cfg"
}
return result, filteredArgs
}

View File

@@ -36,13 +36,14 @@ func llcppsymg(conf []byte) error {
return cmd.Run() return cmd.Run()
} }
func llcppsigfetch(conf []byte, out io.Writer) { func llcppsigfetch(conf []byte, out *io.PipeWriter) {
cmd := exec.Command("llcppsigfetch", "-") cmd := exec.Command("llcppsigfetch", "-")
cmd.Stdin = bytes.NewReader(conf) cmd.Stdin = bytes.NewReader(conf)
cmd.Stdout = out cmd.Stdout = out
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err := cmd.Run() err := cmd.Run()
check(err) check(err)
out.Close()
} }
func gogensig(in io.Reader) error { func gogensig(in io.Reader) error {