Merge pull request #812 from luoliwoshang/llcppsigfetch/refine
llcppsigfetch:cross-platform,log,fix bugs
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
};`,
|
};`,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
63
chore/_xtool/llcppsymg/_cmptest/args_test/args.go
Normal file
63
chore/_xtool/llcppsymg/_cmptest/args_test/args.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
54
chore/_xtool/llcppsymg/_cmptest/args_test/llgo.expect
Normal file
54
chore/_xtool/llcppsymg/_cmptest/args_test/llgo.expect
Normal 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
|
||||||
51
chore/_xtool/llcppsymg/args/args.go
Normal file
51
chore/_xtool/llcppsymg/args/args.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user