Merge pull request #683 from luoliwoshang/llcppsigfetch/basic

llcppsigfetch
This commit is contained in:
xushiwei
2024-09-22 07:58:35 +08:00
committed by GitHub
25 changed files with 6595 additions and 1 deletions

View File

@@ -16,6 +16,169 @@
package main package main
import (
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
)
func main() { func main() {
// TODO(xsw): implement llcppsigfetch tool if len(os.Args) == 1 {
// run with default config file
runFromConfig()
return
}
if os.Args[1] == "--extract" {
runExtract()
} else if os.Args[1] == "--help" || os.Args[1] == "-h" {
printUsage()
} else {
runFromConfig()
}
}
func printUsage() {
fmt.Println("Usage:")
fmt.Println(" llcppsigfetch [<config_file>]")
fmt.Println(" OR")
fmt.Println(" llcppsigfetch --extract <file> <temp> [args...]")
fmt.Println("")
fmt.Println("Options:")
fmt.Println(" [<config_file>]: Path to the configuration file (use '-' for stdin)")
fmt.Println(" If not provided, uses default 'llcppg.cfg'")
fmt.Println("")
fmt.Println(" --extract: Extract information from a single file")
fmt.Println(" <file>: Path to the file to process, or file content if -temp=true")
fmt.Println(" -temp=<bool>: Optional. Set to 'true' if <file> contains file content,")
fmt.Println(" 'false' (default) if it's a file path")
fmt.Println(" -cpp=<bool>: Optional. Set to 'true' if the language is C++ (default: true)")
fmt.Println(" If not present, <file> is a file path")
fmt.Println(" [args]: Optional additional arguments")
fmt.Println(" Default for C++: -x c++")
fmt.Println(" Default for C: -x c")
fmt.Println("")
fmt.Println(" --help, -h: Show this help message")
fmt.Println("")
fmt.Println("Note: The two usage modes are mutually exclusive. Use either [<config_file>] OR --extract, not both.")
}
func runFromConfig() {
cfgFile := "llcppg.cfg"
if len(os.Args) > 1 {
cfgFile = os.Args[1]
}
var data []byte
var err error
if cfgFile == "-" {
data, err = io.ReadAll(os.Stdin)
} else {
data, err = os.ReadFile(cfgFile)
}
check(err)
conf, err := config.GetConf(data)
check(err)
defer conf.Delete()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile)
}
files := getHeaderFiles(conf.CFlags, conf.Include)
context := parse.NewContext(conf.Cplusplus)
err = context.ProcessFiles(files)
check(err)
outputInfo(context)
}
func runExtract() {
if len(os.Args) < 3 {
fmt.Println("Error: Insufficient arguments for --extract")
printUsage()
os.Exit(1)
}
cfg := &parse.Config{
File: os.Args[2],
Args: []string{},
IsCpp: true,
Temp: 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--
default:
cfg.Args = append(cfg.Args, arg)
}
}
converter, err := parse.NewConverter(cfg)
check(err)
_, err = converter.Convert()
check(err)
result := converter.MarshalOutputASTFiles()
cstr := result.Print()
c.Printf(cstr)
cjson.FreeCStr(cstr)
result.Delete()
converter.Dispose()
}
func check(err error) {
if err != nil {
panic(err)
}
}
func getHeaderFiles(cflags string, files []string) []string {
prefix := cflags
prefix = strings.TrimPrefix(prefix, "-I")
var paths []string
for _, f := range files {
paths = append(paths, filepath.Join(prefix, f))
}
return paths
}
func outputInfo(context *parse.Context) {
info := context.Output()
str := info.Print()
defer cjson.FreeCStr(str)
defer info.Delete()
c.Printf(str)
}
func parseBoolArg(arg, name string, defaultValue bool) bool {
parts := strings.SplitN(arg, "=", 2)
if len(parts) != 2 {
fmt.Printf("Warning: Invalid -%s= argument, defaulting to %v\n", name, defaultValue)
return defaultValue
}
value, err := strconv.ParseBool(parts[1])
if err != nil {
fmt.Printf("Warning: Invalid -%s= value '%s', defaulting to %v\n", name, parts[1], defaultValue)
return defaultValue
}
return value
} }

View File

@@ -0,0 +1,955 @@
package parse
import (
"errors"
"fmt"
"os"
"strings"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/llcppg/ast"
"github.com/goplus/llgo/chore/llcppg/token"
)
type FileEntry struct {
Path string
Doc *ast.File
}
type Converter struct {
Files []*FileEntry
FileOrder []string // todo(zzy): more efficient struct
curLoc ast.Location
index *clang.Index
unit *clang.TranslationUnit
typeDecls map[string]ast.Decl // cursorUsr -> ast.Decl
// anonyTypeMap stores mappings for unexpected named declarations in typedefs
// that actually represent anonymous types.
//
// Key: The USR (Unified Symbol Resolution) of the declaration cursor.
// Value: The generated name for the anonymous type.
//
// This map is necessary due to a limitation in libclang where anonymous
// structs, unions, or enums within typedefs are incorrectly reported as
// named declarations. We use this map to keep track of these cases and
// generate appropriate names for them.
//
// Additionally, for all nodes referencing these anonymous types, their
// name references are updated to use the corresponding anonyname from
// this map. This ensures consistent naming across the entire AST for
// these anonymous types.
//
// Example:
// typedef struct { int x; } MyStruct;
anonyTypeMap map[string]bool // cursorUsr
}
var tagMap = map[string]ast.Tag{
"struct": ast.Struct,
"union": ast.Union,
"enum": ast.Enum,
"class": ast.Class,
}
type Config struct {
File string
Temp bool
Args []string
IsCpp bool
}
func NewConverter(config *Config) (*Converter, error) {
index, unit, err := CreateTranslationUnit(config)
if err != nil {
return nil, err
}
return &Converter{
Files: make([]*FileEntry, 0),
index: index,
unit: unit,
anonyTypeMap: make(map[string]bool),
typeDecls: make(map[string]ast.Decl),
}, nil
}
func CreateTranslationUnit(config *Config) (*clang.Index, *clang.TranslationUnit, error) {
// default use the c/c++ standard of clang; c:gnu17 c++:gnu++17
// https://clang.llvm.org/docs/CommandGuide/clang.html
defaultArgs := []string{"-x", "c"}
if config.IsCpp {
defaultArgs = []string{"-x", "c++"}
}
allArgs := append(defaultArgs, config.Args...)
cArgs := make([]*c.Char, len(allArgs))
for i, arg := range allArgs {
cArgs[i] = c.AllocaCStr(arg)
}
index := clang.CreateIndex(0, 0)
var unit *clang.TranslationUnit
if config.Temp {
content := c.AllocaCStr(config.File)
tempFile := &clang.UnsavedFile{
Filename: c.Str("temp.h"),
Contents: content,
Length: c.Ulong(c.Strlen(content)),
}
unit = index.ParseTranslationUnit(
tempFile.Filename,
unsafe.SliceData(cArgs), c.Int(len(cArgs)),
tempFile, 1,
clang.DetailedPreprocessingRecord,
)
} else {
cFile := c.AllocaCStr(config.File)
unit = index.ParseTranslationUnit(
cFile,
unsafe.SliceData(cArgs), c.Int(len(cArgs)),
nil, 0,
clang.DetailedPreprocessingRecord,
)
}
if unit == nil {
return nil, nil, errors.New("failed to parse translation unit")
}
return index, unit, nil
}
func (ct *Converter) Dispose() {
ct.index.Dispose()
ct.unit.Dispose()
}
func (ct *Converter) GetTokens(cursor clang.Cursor) []*ast.Token {
ran := cursor.Extent()
var numTokens c.Uint
var tokens *clang.Token
ct.unit.Tokenize(ran, &tokens, &numTokens)
defer ct.unit.DisposeTokens(tokens, numTokens)
tokensSlice := unsafe.Slice(tokens, int(numTokens))
result := make([]*ast.Token, 0, int(numTokens))
for _, tok := range tokensSlice {
tokStr := ct.unit.Token(tok)
result = append(result, &ast.Token{
Token: toToken(tok),
Lit: c.GoString(tokStr.CStr()),
})
tokStr.Dispose()
}
return result
}
func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
loc := cursor.Location()
var file clang.File
loc.SpellingLocation(&file, nil, nil, nil)
filename := file.FileName()
defer filename.Dispose()
if filename.CStr() == nil {
//todo(zzy): For some built-in macros, there is no file.
ct.curLoc = ast.Location{File: ""}
return
}
filePath := c.GoString(filename.CStr())
ct.curLoc = ast.Location{File: filePath}
}
func (ct *Converter) GetCurFile() *ast.File {
if ct.curLoc.File == "" {
return nil
}
// todo(zzy): more efficient
for i, entry := range ct.Files {
if entry.Path == ct.curLoc.File {
return ct.Files[i].Doc
}
}
newDoc := &ast.File{}
ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc})
return newDoc
}
func (ct *Converter) SetAnonyType(cursor clang.Cursor) {
usr := cursor.USR()
usrStr := c.GoString(usr.CStr())
defer usr.Dispose()
ct.anonyTypeMap[usrStr] = true
}
func (ct *Converter) GetAnonyType(cursor clang.Cursor) (bool, bool) {
usr := cursor.USR()
usrStr := c.GoString(usr.CStr())
defer usr.Dispose()
isAnony, ok := ct.anonyTypeMap[usrStr]
return isAnony, ok
}
func (ct *Converter) SetTypeDecl(cursor clang.Cursor, decl ast.Decl) {
usr := cursor.USR()
usrStr := c.GoString(usr.CStr())
ct.typeDecls[usrStr] = decl
usr.Dispose()
}
func (ct *Converter) GetTypeDecl(cursor clang.Cursor) (ast.Decl, bool) {
usr := cursor.USR()
usrStr := c.GoString(usr.CStr())
decl, ok := ct.typeDecls[usrStr]
usr.Dispose()
return decl, ok
}
func (ct *Converter) CreateDeclBase(cursor clang.Cursor) ast.DeclBase {
base := ast.DeclBase{
Loc: &ct.curLoc,
Parent: ct.BuildScopingExpr(cursor.SemanticParent()),
}
commentGroup, isDoc := ct.ParseCommentGroup(cursor)
if isDoc {
base.Doc = commentGroup
}
return base
}
// extracts and parses comments associated with a given Clang cursor,
// distinguishing between documentation comments and line comments.
// It uses libclang to parse only Doxygen-style comments.
// Reference for Doxygen documentation blocks: https://www.doxygen.nl/manual/docblocks.html
// The function determines whether a comment is a documentation comment or a line comment by
// comparing the range of the comment node with the range of the declaration node in the AST.
// Note: In cases where both documentation comments and line comments conceptually exist,
// only the line comment will be preserved.
func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (comentGroup *ast.CommentGroup, isDoc bool) {
rawComment := cursor.RawCommentText()
defer rawComment.Dispose()
commentGroup := &ast.CommentGroup{}
if rawComment.CStr() != nil {
commentRange := cursor.CommentRange()
cursorRange := cursor.Extent()
isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart())
commentGroup = ct.ParseComment(c.GoString(rawComment.CStr()))
if len(commentGroup.List) > 0 {
return commentGroup, isDoc
}
}
return nil, false
}
func (ct *Converter) ParseComment(rawComment string) *ast.CommentGroup {
lines := strings.Split(rawComment, "\n")
commentGroup := &ast.CommentGroup{}
for _, line := range lines {
commentGroup.List = append(commentGroup.List, &ast.Comment{Text: line})
}
return commentGroup
}
// visit top decls (struct,class,function,enum & macro,include)
func visitTop(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
ct := (*Converter)(clientData)
ct.UpdateLoc(cursor)
curFile := ct.GetCurFile()
if curFile == nil {
return clang.ChildVisit_Continue
}
switch cursor.Kind {
case clang.CursorInclusionDirective:
include := ct.ProcessInclude(cursor)
curFile.Includes = append(curFile.Includes, include)
case clang.CursorMacroDefinition:
macro := ct.ProcessMacro(cursor)
curFile.Macros = append(curFile.Macros, macro)
case clang.CursorEnumDecl:
enum := ct.ProcessEnumDecl(cursor)
curFile.Decls = append(curFile.Decls, enum)
case clang.CursorClassDecl:
classDecl := ct.ProcessClassDecl(cursor)
curFile.Decls = append(curFile.Decls, classDecl)
case clang.CursorStructDecl:
structDecl := ct.ProcessStructDecl(cursor)
curFile.Decls = append(curFile.Decls, structDecl)
case clang.CursorUnionDecl:
unionDecl := ct.ProcessUnionDecl(cursor)
curFile.Decls = append(curFile.Decls, unionDecl)
case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
// Handle functions and class methods (including out-of-class method)
// Example: void MyClass::myMethod() { ... } out-of-class method
curFile.Decls = append(curFile.Decls, ct.ProcessFuncDecl(cursor))
case clang.CursorTypedefDecl:
curFile.Decls = append(curFile.Decls, ct.ProcessTypeDefDecl(cursor))
case clang.CursorNamespace:
clang.VisitChildren(cursor, visitTop, c.Pointer(ct))
}
return clang.ChildVisit_Continue
}
func (ct *Converter) Convert() ([]*FileEntry, error) {
cursor := ct.unit.Cursor()
// visit top decls (struct,class,function & macro,include)
clang.VisitChildren(cursor, visitTop, c.Pointer(ct))
return ct.Files, nil
}
func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
if t.Kind >= clang.TypeFirstBuiltin && t.Kind <= clang.TypeLastBuiltin {
return ct.ProcessBuiltinType(t)
}
if t.Kind == clang.TypeElaborated {
return ct.ProcessElaboratedType(t)
}
var expr ast.Expr
switch t.Kind {
case clang.TypePointer:
expr = &ast.PointerType{X: ct.ProcessType(t.PointeeType())}
case clang.TypeLValueReference:
expr = &ast.LvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
case clang.TypeRValueReference:
expr = &ast.RvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
case clang.TypeFunctionProto, clang.TypeFunctionNoProto:
// treating TypeFunctionNoProto as a general function without parameters
// function type will only collect return type, params will be collected in ProcessFuncDecl
expr = ct.ProcessFunctionType(t)
case clang.TypeConstantArray, clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray:
if t.Kind == clang.TypeConstantArray {
len := (*c.Char)(c.Malloc(unsafe.Sizeof(c.Char(0)) * 20))
c.Sprintf(len, c.Str("%lld"), t.ArraySize())
defer c.Free(unsafe.Pointer(len))
expr = &ast.ArrayType{
Elt: ct.ProcessType(t.ArrayElementType()),
Len: &ast.BasicLit{Kind: ast.IntLit, Value: c.GoString(len)},
}
} else if t.Kind == clang.TypeIncompleteArray {
// incomplete array havent len expr
expr = &ast.ArrayType{
Elt: ct.ProcessType(t.ArrayElementType()),
}
}
}
return expr
}
// For function types, we can only obtain the parameter types, but not the parameter names.
// 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.
func (ct *Converter) ProcessFunctionType(t clang.Type) *ast.FuncType {
// Note: Attempting to get the type declaration for a function type will result in CursorNoDeclFound
// cursor := t.TypeDeclaration()
// This would return CursorNoDeclFound
ret := ct.ProcessType(t.ResultType())
params := &ast.FieldList{}
numArgs := t.NumArgTypes()
for i := 0; i < int(numArgs); i++ {
argType := t.ArgType(c.Uint(i))
params.List = append(params.List, &ast.Field{
Type: ct.ProcessType(argType),
})
}
if t.IsFunctionTypeVariadic() != 0 {
params.List = append(params.List, &ast.Field{
Type: &ast.Variadic{},
})
}
return &ast.FuncType{
Ret: ret,
Params: params,
}
}
func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl {
name := cursor.String()
defer name.Dispose()
typ := ct.ProcessUnderlyingType(cursor)
decl := &ast.TypedefDecl{
DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: c.GoString(name.CStr())},
Type: typ,
}
ct.SetTypeDecl(cursor, decl)
return decl
}
func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
underlyingTyp := cursor.TypedefDeclUnderlyingType()
if underlyingTyp.Kind != clang.TypeElaborated {
return ct.ProcessType(underlyingTyp)
}
referTypeCursor := underlyingTyp.TypeDeclaration()
// If the type decl for the reference already exists in anonyTypeMap
// then the refer has been processed in ProcessElaboratedType
if _, ok := ct.GetAnonyType(referTypeCursor); !ok && isCursorChildOf(referTypeCursor, cursor) {
// Handle unexpected named structures generated from anonymous RecordTypes in Typedefs
// In this case, libclang incorrectly reports an anonymous struct as a named struct
sourceCode := ct.GetTokens(referTypeCursor)
if isAnonymousStructure(sourceCode) {
ct.SetAnonyType(referTypeCursor)
typ, isValidType := ct.GetTypeDecl(referTypeCursor)
if isValidType {
// There will be no anonymous classes,here will execute enum,union,struct
// according to a normal anonymous decl
switch declType := typ.(type) {
case *ast.EnumTypeDecl:
declType.Name = nil
case *ast.TypeDecl:
if declType.Type.Tag != ast.Class {
declType.Name = nil
} else {
// Unreachable: There should be no anonymous classes in this context
fmt.Fprintln(os.Stderr, "unexpect typedef anonymous class %s", declType.Name.Name)
}
}
} else {
// Unreachable:When referencing an anonymous node, its collection must have been completed beforehand
fmt.Fprintln(os.Stderr, "anonymous node not collected before reference")
}
}
}
return ct.ProcessElaboratedType(underlyingTyp)
}
// converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes.
func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
name := cursor.String()
mangledName := cursor.Mangling()
defer name.Dispose()
defer mangledName.Dispose()
// function type will only collect return type
// ProcessType can't get the field names,will collect in follows
funcType, ok := ct.ProcessType(cursor.Type()).(*ast.FuncType)
if !ok {
fmt.Println("failed to process function type")
return nil
}
params := ct.ProcessFieldList(cursor)
funcType.Params = params
mangledNameStr := c.GoString(mangledName.CStr())
if len(mangledNameStr) >= 1 && mangledNameStr[0] == '_' {
mangledNameStr = mangledNameStr[1:]
}
funcDecl := &ast.FuncDecl{
DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: c.GoString(name.CStr())},
Type: funcType,
MangledName: mangledNameStr,
}
if cursor.IsFunctionInlined() != 0 {
funcDecl.IsInline = true
}
if isMethod(cursor) {
ct.ProcessMethodAttributes(cursor, funcDecl)
} else {
if cursor.StorageClass() == clang.SCStatic {
funcDecl.IsStatic = true
}
}
ct.SetTypeDecl(cursor, funcDecl)
return funcDecl
}
// get Methods Attributes
func (ct *Converter) ProcessMethodAttributes(cursor clang.Cursor, fn *ast.FuncDecl) {
if parent := cursor.SemanticParent(); parent.Equal(cursor.LexicalParent()) != 1 {
fn.DeclBase.Parent = ct.BuildScopingExpr(cursor.SemanticParent())
}
switch cursor.Kind {
case clang.CursorDestructor:
fn.IsDestructor = true
case clang.CursorConstructor:
fn.IsConstructor = true
if cursor.IsExplicit() != 0 {
fn.IsExplicit = true
}
}
if cursor.IsStatic() != 0 {
fn.IsStatic = true
}
if cursor.IsVirtual() != 0 || cursor.IsPureVirtual() != 0 {
fn.IsVirtual = true
}
if cursor.IsConst() != 0 {
fn.IsConst = true
}
var numOverridden c.Uint
var overridden *clang.Cursor
cursor.OverriddenCursors(&overridden, &numOverridden)
if numOverridden > 0 {
fn.IsOverride = true
}
overridden.DisposeOverriddenCursors()
}
type visitEnumContext struct {
enum *[]*ast.EnumItem
converter *Converter
}
func visitEnum(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
ctx := (*visitEnumContext)(clientData)
if cursor.Kind == clang.CursorEnumConstantDecl {
name := cursor.String()
val := (*c.Char)(c.Malloc(unsafe.Sizeof(c.Char(0)) * 20))
c.Sprintf(val, c.Str("%lld"), cursor.EnumConstantDeclValue())
defer c.Free(unsafe.Pointer(val))
defer name.Dispose()
enum := &ast.EnumItem{
Name: &ast.Ident{Name: c.GoString(name.CStr())},
Value: &ast.BasicLit{
Kind: ast.IntLit,
Value: c.GoString(val),
},
}
*ctx.enum = append(*ctx.enum, enum)
}
return clang.ChildVisit_Continue
}
func (ct *Converter) ProcessEnumType(cursor clang.Cursor) *ast.EnumType {
items := make([]*ast.EnumItem, 0)
ctx := &visitEnumContext{
enum: &items,
converter: ct,
}
clang.VisitChildren(cursor, visitEnum, c.Pointer(ctx))
return &ast.EnumType{
Items: items,
}
}
func (ct *Converter) ProcessEnumDecl(cursor clang.Cursor) *ast.EnumTypeDecl {
name := cursor.String()
defer name.Dispose()
decl := &ast.EnumTypeDecl{
DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: c.GoString(name.CStr())},
Type: ct.ProcessEnumType(cursor),
}
ct.SetTypeDecl(cursor, decl)
return decl
}
// current only collect macro which defined in file
func (ct *Converter) ProcessMacro(cursor clang.Cursor) *ast.Macro {
name := cursor.String()
defer name.Dispose()
macro := &ast.Macro{
Name: c.GoString(name.CStr()),
Tokens: ct.GetTokens(cursor),
}
return macro
}
func (ct *Converter) ProcessInclude(cursor clang.Cursor) *ast.Include {
name := cursor.String()
defer name.Dispose()
return &ast.Include{Path: c.GoString(name.CStr())}
}
type visitFieldContext struct {
params *ast.FieldList
converter *Converter
}
func (p *visitFieldContext) createBaseField(cursor clang.Cursor) *ast.Field {
field := &ast.Field{
Type: p.converter.ProcessType(cursor.Type()),
}
fieldName := cursor.String()
defer fieldName.Dispose()
commentGroup, isDoc := p.converter.ParseCommentGroup(cursor)
if commentGroup != nil {
if isDoc {
field.Doc = commentGroup
} else {
field.Comment = commentGroup
}
}
if name := fieldName.CStr(); name != nil {
field.Names = []*ast.Ident{{Name: c.GoString(name)}}
}
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
func (ct *Converter) ProcessFieldList(cursor clang.Cursor) *ast.FieldList {
params := &ast.FieldList{}
ctx := &visitFieldContext{
params: params,
converter: ct,
}
clang.VisitChildren(cursor, visitFieldList, c.Pointer(ctx))
if (cursor.Kind == clang.CursorFunctionDecl || isMethod(cursor)) && cursor.IsVariadic() != 0 {
params.List = append(params.List, &ast.Field{
Type: &ast.Variadic{},
})
}
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
func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
methods := make([]*ast.FuncDecl, 0)
ctx := &visitMethodsContext{
methods: &methods,
converter: ct,
}
clang.VisitChildren(cursor, visitMethods, c.Pointer(ctx))
return methods
}
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
anony := cursor.IsAnonymousRecordDecl()
var name *ast.Ident
if anony == 0 {
cursorName := cursor.String()
defer cursorName.Dispose()
name = &ast.Ident{Name: c.GoString(cursorName.CStr())}
}
decl := &ast.TypeDecl{
DeclBase: ct.CreateDeclBase(cursor),
Name: name,
Type: ct.ProcessRecordType(cursor),
}
ct.SetTypeDecl(cursor, decl)
return decl
}
func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl {
return ct.ProcessRecordDecl(cursor)
}
func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl {
return ct.ProcessRecordDecl(cursor)
}
func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl {
// Pushing class scope before processing its type and popping after
base := ct.CreateDeclBase(cursor)
typ := ct.ProcessRecordType(cursor)
decl := &ast.TypeDecl{
DeclBase: base,
Name: &ast.Ident{Name: c.GoString(cursor.String().CStr())},
Type: typ,
}
ct.SetTypeDecl(cursor, decl)
return decl
}
func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
return &ast.RecordType{
Tag: toTag(cursor.Kind),
Fields: ct.ProcessFieldList(cursor),
Methods: ct.ProcessMethods(cursor),
}
}
// process ElaboratedType Reference
//
// 1. Named elaborated type references:
// - Examples: struct MyStruct, union MyUnion, class MyClass, enum MyEnum
// - Handling: Constructed as TagExpr or ScopingExpr references
//
// 2. Anonymous elaborated type references:
// - Examples: struct { int x; int y; }, union { int a; float b; }
// - Handling: Retrieve their corresponding concrete types
func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr {
name := t.String()
defer name.Dispose()
decl := t.TypeDeclaration()
isAnony, ok := ct.GetAnonyType(decl)
if decl.IsAnonymous() != 0 || isAnony && ok {
// anonymous type refer (except anonymous RecordType&EnumType in TypedefDecl)
if decl.Kind == clang.CursorEnumDecl {
return ct.ProcessEnumType(decl)
}
return ct.ProcessRecordType(decl)
}
typeName := c.GoString(name.CStr())
// for elaborated type, it could have a tag description
// like struct A, union B, class C, enum D
parts := strings.SplitN(typeName, " ", 2)
if len(parts) == 2 {
if tagValue, ok := tagMap[parts[0]]; ok {
return &ast.TagExpr{
Tag: tagValue,
Name: ct.BuildScopingExpr(decl),
}
}
}
return ct.BuildScopingExpr(decl)
}
func (ct *Converter) ProcessBuiltinType(t clang.Type) *ast.BuiltinType {
kind := ast.Void
var flags ast.TypeFlag
switch t.Kind {
case clang.TypeVoid:
kind = ast.Void
case clang.TypeBool:
kind = ast.Bool
case clang.TypeCharU, clang.TypeUChar, clang.TypeCharS, clang.TypeSChar:
kind = ast.Char
case clang.TypeChar16:
kind = ast.Char16
case clang.TypeChar32:
kind = ast.Char32
case clang.TypeWChar:
kind = ast.WChar
case clang.TypeShort, clang.TypeUShort:
kind = ast.Int
flags |= ast.Short
case clang.TypeInt, clang.TypeUInt:
kind = ast.Int
case clang.TypeLong, clang.TypeULong:
kind = ast.Int
flags |= ast.Long
case clang.TypeLongLong, clang.TypeULongLong:
kind = ast.Int
flags |= ast.LongLong
case clang.TypeInt128, clang.TypeUInt128:
kind = ast.Int128
case clang.TypeFloat:
kind = ast.Float
case clang.TypeHalf, clang.TypeFloat16:
kind = ast.Float16
case clang.TypeDouble:
kind = ast.Float
flags |= ast.Double
case clang.TypeLongDouble:
kind = ast.Float
flags |= ast.Long | ast.Double
case clang.TypeFloat128:
kind = ast.Float128
case clang.TypeComplex:
kind = ast.Complex
complexKind := t.ElementType().Kind
if complexKind == clang.TypeLongDouble {
flags |= ast.Long | ast.Double
} else if complexKind == clang.TypeDouble {
flags |= ast.Double
}
// float complfex flag is not set
default:
// like IBM128,NullPtr,Accum
kindStr := t.Kind.String()
defer kindStr.Dispose()
fmt.Fprintln(os.Stderr, "todo: unknown builtin type:", c.GoString(kindStr.CStr()))
}
if IsExplicitSigned(t) {
flags |= ast.Signed
} else if IsExplicitUnsigned(t) {
flags |= ast.Unsigned
}
return &ast.BuiltinType{
Kind: kind,
Flags: flags,
}
}
// Constructs a complete scoping expression by traversing the semantic parents, starting from the given clang.Cursor
// For anonymous decl of typedef references, use their anonymous name
func (ct *Converter) BuildScopingExpr(cursor clang.Cursor) ast.Expr {
parts := ct.BuildScopingParts(cursor)
return buildScopingFromParts(parts)
}
func (ct *Converter) BuildScopingParts(cursor clang.Cursor) []string {
var parts []string
// Traverse up the semantic parents
for cursor.IsNull() != 1 && cursor.Kind != clang.CursorTranslationUnit {
name := cursor.String()
qualified := c.GoString(name.CStr())
parts = append([]string{qualified}, parts...)
cursor = cursor.SemanticParent()
name.Dispose()
}
return parts
}
func (ct *Converter) MarshalASTFiles() *cjson.JSON {
return MarshalASTFiles(ct.Files)
}
func (ct *Converter) MarshalOutputASTFiles() *cjson.JSON {
return MarshalOutputASTFiles(ct.Files)
}
func IsExplicitSigned(t clang.Type) bool {
return t.Kind == clang.TypeCharS || t.Kind == clang.TypeSChar
}
func IsExplicitUnsigned(t clang.Type) bool {
return t.Kind == clang.TypeCharU || t.Kind == clang.TypeUChar ||
t.Kind == clang.TypeUShort || t.Kind == clang.TypeUInt ||
t.Kind == clang.TypeULong || t.Kind == clang.TypeULongLong ||
t.Kind == clang.TypeUInt128
}
func toTag(kind clang.CursorKind) ast.Tag {
switch kind {
case clang.CursorStructDecl:
return ast.Struct
case clang.CursorUnionDecl:
return ast.Union
case clang.CursorClassDecl:
return ast.Class
default:
panic(fmt.Sprintf("Unexpected cursor kind in toTag: %v", kind))
}
}
func toToken(tok clang.Token) token.Token {
if tok.Kind() < clang.Punctuation || tok.Kind() > clang.Comment {
return token.ILLEGAL
} else {
return token.Token(tok.Kind() + 1)
}
}
func isMethod(cursor clang.Cursor) bool {
return cursor.Kind == clang.CursorCXXMethod || cursor.Kind == clang.CursorConstructor || cursor.Kind == clang.CursorDestructor
}
func buildScopingFromParts(parts []string) ast.Expr {
if len(parts) == 0 {
return nil
}
var expr ast.Expr = &ast.Ident{Name: parts[0]}
for _, part := range parts[1:] {
expr = &ast.ScopingExpr{
Parent: expr,
X: &ast.Ident{Name: part},
}
}
return expr
}
// isCursorChildOf checks if the child cursor is contained within the parent cursor.
// This function is necessary because libclang doesn't correctly report the lexical
// or semantic parent for anonymous structs inside typedefs. By comparing source ranges,
// we can determine if one cursor is nested inside another.
func isCursorChildOf(child, parent clang.Cursor) bool {
return isRangeChildOf(child.Extent(), parent.Extent())
}
func isRangeChildOf(childRange, parentRange clang.SourceRange) bool {
return getOffset(childRange.RangeStart()) >= getOffset(parentRange.RangeStart()) &&
getOffset(childRange.RangeEnd()) <= getOffset(parentRange.RangeEnd())
}
func getOffset(location clang.SourceLocation) c.Uint {
var offset c.Uint
location.SpellingLocation(nil, nil, nil, &offset)
return offset
}
// checks if the source code represents an actual anonymous structure
func isAnonymousStructure(sourceCode []*ast.Token) bool {
_, isValidTag := tagMap[sourceCode[0].Lit]
return sourceCode[0].Token == token.KEYWORD &&
isValidTag &&
sourceCode[1].Token == token.PUNCT &&
sourceCode[1].Lit == "{"
}

View File

@@ -0,0 +1,94 @@
package cvttest
import (
"fmt"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse"
)
func RunTest(testName string, testCases []string) {
for i, content := range testCases {
converter, err := parse.NewConverter(&parse.Config{
File: content,
Temp: true,
IsCpp: true,
})
if err != nil {
panic(err)
}
_, err = converter.Convert()
if err != nil {
panic(err)
}
result := converter.MarshalASTFiles()
str := result.Print()
c.Printf(c.Str("%s Case %d:\n%s\n\n"), c.AllocaCStr(testName), c.Int(i+1), str)
cjson.FreeCStr(str)
result.Delete()
converter.Dispose()
}
}
type GetTypeOptions struct {
TypeCode string // e.g. "char*", "char**"
// ExpectTypeKind specifies the expected type kind (optional)
// Use clang.Type_Invalid to accept any type (default behavior)
// *For complex types (when <complex.h> is included), specifying this is crucial
// to filter out the correct type, as there will be multiple VarDecl fields present
ExpectTypeKind clang.TypeKind
// Args contains additional compilation arguments passed to Clang (optional)
// These are appended after the default language-specific arguments
// Example: []string{"-std=c++11"}
Args []string
// IsCpp indicates whether the code should be treated as C++ (true) or C (false)
// This affects the default language arguments passed to Clang:
// - For C++: []string{"-x", "c++"}
// - For C: []string{"-x", "c"}
// *For complex C types, C Must be specified
IsCpp bool
}
// GetType returns the clang.Type of the given type code
// Need to dispose the index and unit after using
// e.g. index.Dispose(), unit.Dispose()
func GetType(option *GetTypeOptions) (clang.Type, *clang.Index, *clang.TranslationUnit) {
code := fmt.Sprintf("%s placeholder;", option.TypeCode)
index, unit, err := parse.CreateTranslationUnit(&parse.Config{
File: code,
Temp: true,
Args: option.Args,
IsCpp: option.IsCpp,
})
if err != nil {
panic(err)
}
cursor := unit.Cursor()
visitType := &typeVisitData{typ: &clang.Type{}, expectTypeKind: option.ExpectTypeKind}
clang.VisitChildren(cursor, typeVisit, unsafe.Pointer(visitType))
return *visitType.typ, index, unit
}
type typeVisitData struct {
typ *clang.Type
expectTypeKind clang.TypeKind
}
func typeVisit(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
visitData := (*typeVisitData)(clientData)
if cursor.Kind == clang.CursorVarDecl && (visitData.expectTypeKind == clang.TypeInvalid || cursor.Type().Kind == visitData.expectTypeKind) {
*visitData.typ = cursor.Type()
return clang.ChildVisit_Break
}
return clang.ChildVisit_Continue
}

View File

@@ -0,0 +1,53 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestClassDecl()
}
func TestClassDecl() {
testCases := []string{
`class A {
public:
int a;
int b;
};`,
`class A {
public:
static int a;
int b;
float foo(int a,double b);
private:
static void bar();
protected:
void bar2();
};`,
`class A {
public:
A();
explicit A();
~A();
static inline void foo();
};`,
`class Base {
public:
Base();
virtual ~Base();
virtual void foo();
};
class Derived : public Base {
public:
Derived();
~Derived() override;
void foo() override;
};
`,
`namespace A{
class Foo{}
}
void A::Foo::bar();
`,
}
test.RunTest("TestClassDecl", testCases)
}

View File

@@ -0,0 +1,713 @@
#stdout
TestClassDecl Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestClassDecl Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": true,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "A"
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN1A3fooEid",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 8,
"Flags": 16
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 8,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}]
}
}],
"includes": [],
"macros": []
}
}
TestClassDecl Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": null
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "A"
},
"Name": {
"_Type": "Ident",
"Name": "A"
},
"MangledName": "_ZN1AC1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": true,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "A"
},
"Name": {
"_Type": "Ident",
"Name": "A"
},
"MangledName": "_ZN1AC1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": true,
"IsConstructor": true,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "A"
},
"Name": {
"_Type": "Ident",
"Name": "~A"
},
"MangledName": "_ZN1AD1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": true,
"IsVirtual": false,
"IsOverride": false
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "A"
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN1A3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": true,
"IsStatic": true,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}]
}
}],
"includes": [],
"macros": []
}
}
TestClassDecl Case 4:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "Base"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": null
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "Base"
},
"Name": {
"_Type": "Ident",
"Name": "Base"
},
"MangledName": "_ZN4BaseC1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": true,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "Base"
},
"Name": {
"_Type": "Ident",
"Name": "~Base"
},
"MangledName": "_ZN4BaseD1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": true,
"IsVirtual": true,
"IsOverride": false
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "Base"
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN4Base3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": true,
"IsOverride": false
}]
}
}, {
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "Derived"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": null
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "Derived"
},
"Name": {
"_Type": "Ident",
"Name": "Derived"
},
"MangledName": "_ZN7DerivedC1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": true,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "Derived"
},
"Name": {
"_Type": "Ident",
"Name": "~Derived"
},
"MangledName": "_ZN7DerivedD1Ev",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": true,
"IsVirtual": true,
"IsOverride": true
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "Derived"
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN7Derived3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": true,
"IsOverride": true
}]
}
}],
"includes": [],
"macros": []
}
}
TestClassDecl Case 5:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "A"
},
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": null
},
"Methods": []
}
}, {
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "Foo"
},
"Parent": {
"_Type": "Ident",
"Name": "A"
}
},
"Name": {
"_Type": "Ident",
"Name": "bar"
},
"MangledName": "_ZN1A3Foo3barEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,78 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestDoc()
}
func TestDoc() {
testCases := []string{
`
// not read doc 1
void foo();`,
`
/* not read doc 2 */
void foo();`,
`
/// doc
void foo();`,
`
/** doc */
void foo();`,
`
/*! doc */
void foo();`,
`
/// doc 1
/// doc 2
void foo();`,
`
/*! doc 1 */
/*! doc 2 */
void foo();`,
`
/** doc 1 */
/** doc 1 */
void foo();`,
`
/**
* doc 1
* doc 2
*/
void foo();`,
`
struct Foo {
/// doc
int x;
int y; ///< comment
/**
* field doc (parse ignore with comment in same cursor)
*/
int z; /*!< comment */
};
`,
`
class Doc
{
public:
/**
* static field doc
*/
static int x;
static int y; /*!< static field comment */
/**
* field doc
*/
int a;
int b; ///< field comment
/**
* method doc
*/
void Foo();
protected:
int value; /*!< protected field comment */
};`,
}
test.RunTest("TestDoc", testCases)
}

View File

@@ -0,0 +1,742 @@
#stdout
TestDoc Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/// doc"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 4:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/** doc */"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 5:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/*! doc */"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 6:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/// doc 1"
}, {
"_Type": "Comment",
"Text": "/// doc 2"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 7:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/*! doc 1 */"
}, {
"_Type": "Comment",
"Text": "/*! doc 2 */"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 8:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/** doc 1 */"
}, {
"_Type": "Comment",
"Text": "/** doc 1 */"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 9:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/**"
}, {
"_Type": "Comment",
"Text": " * doc 1"
}, {
"_Type": "Comment",
"Text": " * doc 2"
}, {
"_Type": "Comment",
"Text": " */"
}]
},
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestDoc Case 10:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/// doc"
}]
},
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "x"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "///< comment"
}]
},
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "y"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/*!< comment */"
}]
},
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "z"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestDoc Case 11:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "Doc"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/** "
}, {
"_Type": "Comment",
"Text": " * static field doc"
}, {
"_Type": "Comment",
"Text": " */"
}]
},
"Comment": null,
"IsStatic": true,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "x"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/*!< static field comment */"
}]
},
"IsStatic": true,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "y"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/** "
}, {
"_Type": "Comment",
"Text": " * field doc"
}, {
"_Type": "Comment",
"Text": " */"
}]
},
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "///< field comment"
}]
},
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/*!< protected field comment */"
}]
},
"IsStatic": false,
"Access": 2,
"Names": [{
"_Type": "Ident",
"Name": "value"
}]
}]
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": {
"_Type": "CommentGroup",
"List": [{
"_Type": "Comment",
"Text": "/** "
}, {
"_Type": "Comment",
"Text": " * method doc"
}, {
"_Type": "Comment",
"Text": " */"
}]
},
"Parent": {
"_Type": "Ident",
"Name": "Doc"
},
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"MangledName": "_ZN3Doc3FooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}]
}
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,28 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestEnumDecl()
}
func TestEnumDecl() {
testCases := []string{
`enum Foo {
a,
b,
c,
};`,
`enum Foo {
a = 1,
b = 2,
c = 4,
};`,
`enum Foo {
a = 1,
b,
c,
};`,
}
test.RunTest("TestEnumDecl", testCases)
}

View File

@@ -0,0 +1,182 @@
#stdout
TestEnumDecl Case 1:
{
"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 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": "1"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "b"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "2"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "c"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "4"
}
}]
}
}],
"includes": [],
"macros": []
}
}
TestEnumDecl Case 3:
{
"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": "1"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "b"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "2"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "c"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "3"
}
}]
}
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,18 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestFuncDecl()
}
func TestFuncDecl() {
testCases := []string{
`void foo();`,
`void foo(int a);`,
`void foo(int a,...);`,
`float* foo(int a,double b);`,
`static inline int add(int a, int b);`,
}
test.RunTest("TestFuncDecl", testCases)
}

View File

@@ -0,0 +1,323 @@
#stdout
TestFuncDecl Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3fooi",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}]
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3fooiz",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "Variadic"
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": null
}]
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 4:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3fooid",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 8,
"Flags": 16
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Ret": {
"_Type": "PointerType",
"X": {
"_Type": "BuiltinType",
"Kind": 8,
"Flags": 0
}
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 5:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "add"
},
"MangledName": "_ZL3addii",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
},
"IsInline": true,
"IsStatic": true,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,291 @@
#stdout
TestScope Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_Z3foov",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestScope Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "a"
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN1a3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestScope Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "b"
},
"Parent": {
"_Type": "Ident",
"Name": "a"
}
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN1a1b3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}],
"includes": [],
"macros": []
}
}
TestScope Case 4:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "a"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": null
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "a"
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN1a3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}]
}
}],
"includes": [],
"macros": []
}
}
TestScope Case 5:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "Ident",
"Name": "a"
},
"Name": {
"_Type": "Ident",
"Name": "b"
},
"Type": {
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": null
},
"Methods": [{
"_Type": "FuncDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": {
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "b"
},
"Parent": {
"_Type": "Ident",
"Name": "a"
}
},
"Name": {
"_Type": "Ident",
"Name": "foo"
},
"MangledName": "_ZN1a1b3fooEv",
"Type": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": null
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 0,
"Flags": 0
}
},
"IsInline": false,
"IsStatic": false,
"IsConst": false,
"IsExplicit": false,
"IsConstructor": false,
"IsDestructor": false,
"IsVirtual": false,
"IsOverride": false
}]
}
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,32 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestScope()
}
func TestScope() {
testCases := []string{
`void foo();`,
`namespace a {
void foo();
}`,
`namespace a {
namespace b {
void foo();
}
}`,
`class a {
public:
void foo();
};`,
`namespace a {
class b {
public:
void foo();
};
}`,
}
test.RunTest("TestScope", testCases)
}

View File

@@ -0,0 +1,375 @@
#stdout
TestStructDecl Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": null,
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestStructDecl Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestStructDecl Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestStructDecl Case 4:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "PointerType",
"X": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": null
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": null
}]
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
}
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "Foo"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestStructDecl Case 5:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "Person"
},
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "age"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "year"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "day"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "month"
}]
}]
},
"Methods": []
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "birthday"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,35 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestStructDecl()
}
func TestStructDecl() {
testCases := []string{
`struct {
int a;
};`,
`struct A {
int a;
int b;
};`,
`struct A {
int a, b;
};`,
`struct A {
int a;
int (*Foo)(int, int);
};`,
`struct Person {
int age;
struct {
int year;
int day;
int month;
} birthday;
};`,
}
test.RunTest("TestStructDecl", testCases)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestTypeDefDecl()
}
func TestTypeDefDecl() {
testCases := []string{
`typedef int INT;`,
`typedef int INT;
typedef INT STANDARD_INT;`,
`typedef int INT,*IntPtr,IntArr[];`,
`typedef int (*Foo)(int, int, ...);`,
`typedef int (*Foo)(int, int),(*Bar)(void*,void*);`,
`namespace A {
typedef class Foo{
int x;
} MyClass,*MyClassPtr,MyClassArray[];
}`,
`typedef struct {
int x;
} MyStruct`,
`typedef union {
int x;
} MyUnion`,
`typedef enum {
RED,
GREEN,
BLUE
} MyEnum`,
`typedef struct {
int x;
} MyStruct,MyStruct2,*StructPtr, StructArr[];`,
`namespace A{
namespace B{
typedef struct {
int x;
} MyStruct,MyStruct2,*StructPtr, StructArr[];
}
}`,
}
test.RunTest("TestTypeDefDecl", testCases)
}

View File

@@ -0,0 +1,234 @@
#stdout
TestUnionDecl Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": null,
"Type": {
"_Type": "RecordType",
"Tag": 1,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestUnionDecl Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "A"
},
"Type": {
"_Type": "RecordType",
"Tag": 1,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "a"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "b"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
TestUnionDecl Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "OuterUnion"
},
"Type": {
"_Type": "RecordType",
"Tag": 1,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "i"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 8,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "f"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "RecordType",
"Tag": 1,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "c"
}]
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 32
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "s"
}]
}]
},
"Methods": []
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "inner"
}]
}]
},
"Methods": []
}
}],
"includes": [],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,29 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestUnionDecl()
}
func TestUnionDecl() {
testCases := []string{
`union {
int a;
int b;
};`,
`union A {
int a;
int b;
};`,
`union OuterUnion {
int i;
float f;
union {
char c;
short s;
} inner;
};`,
}
test.RunTest("TestUnionDecl", testCases)
}

View File

@@ -0,0 +1,124 @@
#stdout
TestDefine Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [],
"includes": [],
"macros": [{
"_Type": "Macro",
"Name": "DEBUG",
"Tokens": [{
"_Type": "Token",
"Token": 3,
"Lit": "DEBUG"
}]
}]
}
}
TestDefine Case 2:
{
"temp.h": {
"_Type": "File",
"decls": [],
"includes": [],
"macros": [{
"_Type": "Macro",
"Name": "OK",
"Tokens": [{
"_Type": "Token",
"Token": 3,
"Lit": "OK"
}, {
"_Type": "Token",
"Token": 4,
"Lit": "1"
}]
}]
}
}
TestDefine Case 3:
{
"temp.h": {
"_Type": "File",
"decls": [],
"includes": [],
"macros": [{
"_Type": "Macro",
"Name": "SQUARE",
"Tokens": [{
"_Type": "Token",
"Token": 3,
"Lit": "SQUARE"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "("
}, {
"_Type": "Token",
"Token": 3,
"Lit": "x"
}, {
"_Type": "Token",
"Token": 1,
"Lit": ")"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "("
}, {
"_Type": "Token",
"Token": 1,
"Lit": "("
}, {
"_Type": "Token",
"Token": 3,
"Lit": "x"
}, {
"_Type": "Token",
"Token": 1,
"Lit": ")"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "*"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "("
}, {
"_Type": "Token",
"Token": 3,
"Lit": "x"
}, {
"_Type": "Token",
"Token": 1,
"Lit": ")"
}, {
"_Type": "Token",
"Token": 1,
"Lit": ")"
}]
}]
}
}
TestInclude Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [],
"includes": [{
"_Type": "Include",
"Path": "foo.h"
}],
"macros": []
}
}
#stderr
#exit 0

View File

@@ -0,0 +1,27 @@
package main
import (
test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
)
func main() {
TestDefine()
TestInclude()
}
func TestDefine() {
testCases := []string{
`#define DEBUG`,
`#define OK 1`,
`#define SQUARE(x) ((x) * (x))`,
}
test.RunTest("TestDefine", testCases)
}
func TestInclude() {
testCases := []string{
`#include "foo.h"`,
// `#include <limits.h>`, // Standard libraries are mostly platform-dependent
}
test.RunTest("TestInclude", testCases)
}

View File

@@ -0,0 +1,350 @@
#stdout
Void:flags:0 kind:0
Bool:flags:0 kind:1
Char_S:flags:1 kind:2
Char_U:flags:2 kind:2
Char16:flags:0 kind:3
Char32:flags:0 kind:4
WChar:flags:0 kind:5
Short:flags:32 kind:6
UShort:flags:34 kind:6
Int:flags:0 kind:6
UInt:flags:2 kind:6
Long:flags:4 kind:6
ULong:flags:6 kind:6
LongLong:flags:8 kind:6
ULongLong:flags:10 kind:6
Int128:flags:0 kind:7
UInt128:flags:2 kind:7
Float:flags:0 kind:8
Half:flags:0 kind:9
Float16:flags:0 kind:9
Double:flags:16 kind:8
LongDouble:flags:20 kind:8
Float128:flags:0 kind:10
Complex:flags:0 kind:11
Complex:flags:16 kind:11
Complex:flags:20 kind:11
Unknown:flags:0 kind:0
Type: char *:
{
"_Type": "PointerType",
"X": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
}
}
Type: char ***:
{
"_Type": "PointerType",
"X": {
"_Type": "PointerType",
"X": {
"_Type": "PointerType",
"X": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
}
}
}
}
Type: char[]:
{
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
},
"Len": null
}
Type: char[10]:
{
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
},
"Len": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "10"
}
}
Type: char[3][4]:
{
"_Type": "ArrayType",
"Elt": {
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
},
"Len": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "4"
}
},
"Len": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "3"
}
}
Type: int &:
{
"_Type": "LvalueRefType",
"X": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
}
Type: int &&:
{
"_Type": "RvalueRefType",
"X": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
}
Type: Foo:
{
"_Type": "Ident",
"Name": "Foo"
}
Type: struct Foo:
{
"_Type": "TagExpr",
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Tag": 0
}
Type: struct (unnamed struct at temp.h:1:1):
{
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "x"
}]
}]
},
"Methods": []
}
Type: Foo:
{
"_Type": "Ident",
"Name": "Foo"
}
Type: union Foo:
{
"_Type": "TagExpr",
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Tag": 1
}
Type: union (unnamed union at temp.h:1:1):
{
"_Type": "RecordType",
"Tag": 1,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "x"
}]
}]
},
"Methods": []
}
Type: Foo:
{
"_Type": "Ident",
"Name": "Foo"
}
Type: enum Foo:
{
"_Type": "TagExpr",
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Tag": 2
}
Type: enum (unnamed enum at temp.h:1:1):
{
"_Type": "EnumType",
"Items": [{
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "x"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "42"
}
}]
}
Type: Foo:
{
"_Type": "Ident",
"Name": "Foo"
}
Type: class Foo:
{
"_Type": "TagExpr",
"Name": {
"_Type": "Ident",
"Name": "Foo"
},
"Tag": 3
}
Type: class (unnamed class at temp.h:1:1):
{
"_Type": "RecordType",
"Tag": 3,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 3,
"Names": [{
"_Type": "Ident",
"Name": "x"
}]
}]
},
"Methods": []
}
Type: a::b::c:
{
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "c"
},
"Parent": {
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "b"
},
"Parent": {
"_Type": "Ident",
"Name": "a"
}
}
}
Type: class a::b::c:
{
"_Type": "TagExpr",
"Name": {
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "c"
},
"Parent": {
"_Type": "ScopingExpr",
"X": {
"_Type": "Ident",
"Name": "b"
},
"Parent": {
"_Type": "Ident",
"Name": "a"
}
}
},
"Tag": 3
}
Type: int (*)(int, char):
{
"_Type": "PointerType",
"X": {
"_Type": "FuncType",
"Params": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": null
}, {
"_Type": "Field",
"Type": {
"_Type": "BuiltinType",
"Kind": 2,
"Flags": 1
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 0,
"Names": null
}]
},
"Ret": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
}
}
#stderr
todo: unknown builtin type: Ibm128
#exit 0

View File

@@ -0,0 +1,175 @@
package main
import (
"fmt"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse"
test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
"github.com/goplus/llgo/chore/llcppg/ast"
)
func main() {
TestBuiltinType()
TestNonBuiltinTypes()
}
func TestBuiltinType() {
tests := []struct {
name string
typ clang.Type
expected ast.BuiltinType
}{
{"Void", btType(clang.TypeVoid), ast.BuiltinType{Kind: ast.Void}},
{"Bool", btType(clang.TypeBool), ast.BuiltinType{Kind: ast.Bool}},
{"Char_S", btType(clang.TypeCharS), ast.BuiltinType{Kind: ast.Char, Flags: ast.Signed}},
{"Char_U", btType(clang.TypeCharU), ast.BuiltinType{Kind: ast.Char, Flags: ast.Unsigned}},
{"Char16", btType(clang.TypeChar16), ast.BuiltinType{Kind: ast.Char16}},
{"Char32", btType(clang.TypeChar32), ast.BuiltinType{Kind: ast.Char32}},
{"WChar", btType(clang.TypeWChar), ast.BuiltinType{Kind: ast.WChar}},
{"Short", btType(clang.TypeShort), ast.BuiltinType{Kind: ast.Int, Flags: ast.Short}},
{"UShort", btType(clang.TypeUShort), ast.BuiltinType{Kind: ast.Int, Flags: ast.Short | ast.Unsigned}},
{"Int", btType(clang.TypeInt), ast.BuiltinType{Kind: ast.Int}},
{"UInt", btType(clang.TypeUInt), ast.BuiltinType{Kind: ast.Int, Flags: ast.Unsigned}},
{"Long", btType(clang.TypeLong), ast.BuiltinType{Kind: ast.Int, Flags: ast.Long}},
{"ULong", btType(clang.TypeULong), ast.BuiltinType{Kind: ast.Int, Flags: ast.Long | ast.Unsigned}},
{"LongLong", btType(clang.TypeLongLong), ast.BuiltinType{Kind: ast.Int, Flags: ast.LongLong}},
{"ULongLong", btType(clang.TypeULongLong), ast.BuiltinType{Kind: ast.Int, Flags: ast.LongLong | ast.Unsigned}},
{"Int128", btType(clang.TypeInt128), ast.BuiltinType{Kind: ast.Int128}},
{"UInt128", btType(clang.TypeUInt128), ast.BuiltinType{Kind: ast.Int128, Flags: ast.Unsigned}},
{"Float", btType(clang.TypeFloat), ast.BuiltinType{Kind: ast.Float}},
{"Half", btType(clang.TypeHalf), ast.BuiltinType{Kind: ast.Float16}},
{"Float16", btType(clang.TypeFloat16), ast.BuiltinType{Kind: ast.Float16}},
{"Double", btType(clang.TypeDouble), ast.BuiltinType{Kind: ast.Float, Flags: ast.Double}},
{"LongDouble", btType(clang.TypeLongDouble), ast.BuiltinType{Kind: ast.Float, Flags: ast.Long | ast.Double}},
{"Float128", btType(clang.TypeFloat128), ast.BuiltinType{Kind: ast.Float128}},
{"Complex", getComplexType(0), ast.BuiltinType{Kind: ast.Complex}},
{"Complex", getComplexType(ast.Double), ast.BuiltinType{Flags: ast.Double, Kind: ast.Complex}},
{"Complex", getComplexType(ast.Long | ast.Double), ast.BuiltinType{Flags: ast.Long | ast.Double, Kind: ast.Complex}},
{"Unknown", btType(clang.TypeIbm128), ast.BuiltinType{Kind: ast.Void}},
}
converter := &parse.Converter{}
converter.Convert()
for _, bt := range tests {
res := converter.ProcessBuiltinType(bt.typ)
if res.Kind != bt.expected.Kind {
fmt.Printf("%s Kind mismatch:got %d want %d, \n", bt.name, res.Kind, bt.expected.Kind)
}
if res.Flags != bt.expected.Flags {
fmt.Printf("%s Flags mismatch:got %d,want %d\n", bt.name, res.Flags, bt.expected.Flags)
}
fmt.Printf("%s:flags:%d kind:%d\n", bt.name, res.Flags, res.Kind)
}
}
func TestNonBuiltinTypes() {
tests := []string{
"char*",
"char***",
"char[]",
"char[10]",
"char[3][4]",
"int&",
"int&&",
`struct Foo {};
Foo`,
`struct Foo {};
struct Foo`,
`struct {
int x;
}`,
`union Foo {};
Foo`,
`union Foo {};
union Foo`,
`union {
int x;
}`,
`enum Foo {};
Foo`,
`enum Foo {};
enum Foo`,
`enum { x = 42 }`,
`class Foo {};
Foo`,
`class Foo {};
class Foo`,
`class {
int x;
}`,
`namespace a {
namespace b {
class c {
};
}
}
a::b::c`,
`namespace a {
namespace b {
class c {
};
}
}
class a::b::c`,
`int (*p)(int, char);`,
}
for _, t := range tests {
typ, index, unit := test.GetType(&test.GetTypeOptions{
TypeCode: t,
IsCpp: true,
})
converter := &parse.Converter{}
expr := converter.ProcessType(typ)
json := parse.MarshalASTExpr(expr)
str := json.Print()
typstr := typ.String()
c.Printf(c.Str("Type: %s:\n"), typstr)
c.Printf(c.Str("%s\n"), str)
typstr.Dispose()
cjson.FreeCStr(str)
json.Delete()
index.Dispose()
unit.Dispose()
}
}
func btType(kind clang.TypeKind) clang.Type {
return clang.Type{Kind: kind}
}
// get complex type from source code parsed
func getComplexType(flag ast.TypeFlag) clang.Type {
var typeStr string
if flag&(ast.Long|ast.Double) == (ast.Long | ast.Double) {
typeStr = "long double"
} else if flag&ast.Double != 0 {
typeStr = "double"
} else {
typeStr = "float"
}
code := fmt.Sprintf("#include <complex.h>\n%s complex", typeStr)
// todo(zzy):free index and unit after test
typ, _, _ := test.GetType(&test.GetTypeOptions{
TypeCode: code,
ExpectTypeKind: clang.TypeComplex,
IsCpp: false,
})
return typ
}

View File

@@ -0,0 +1,273 @@
package parse
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/llcppg/ast"
)
func MarshalOutputASTFiles(files []*FileEntry) *cjson.JSON {
root := cjson.Array()
for _, entry := range files {
f := cjson.Object()
path := cjson.String(c.AllocaCStr(entry.Path))
f.SetItem(c.Str("path"), path)
f.SetItem(c.Str("doc"), MarshalASTFile(entry.Doc))
root.AddItem(f)
}
return root
}
func MarshalASTFiles(files []*FileEntry) *cjson.JSON {
root := cjson.Object()
for _, entry := range files {
root.SetItem(c.AllocaCStr(entry.Path), MarshalASTFile(entry.Doc))
}
return root
}
func MarshalDeclList(list []ast.Decl) *cjson.JSON {
root := cjson.Array()
for _, item := range list {
root.AddItem(MarshalASTDecl(item))
}
return root
}
func MarshalFieldList(list []*ast.Field) *cjson.JSON {
if list == nil {
return cjson.Null()
}
root := cjson.Array()
for _, item := range list {
root.AddItem(MarshalASTExpr(item))
}
return root
}
func MarshalIncludeList(list []*ast.Include) *cjson.JSON {
root := cjson.Array()
for _, item := range list {
include := cjson.Object()
include.SetItem(c.Str("_Type"), stringField("Include"))
include.SetItem(c.Str("Path"), stringField(item.Path))
root.AddItem(include)
}
return root
}
func MarshalMacroList(list []*ast.Macro) *cjson.JSON {
root := cjson.Array()
for _, item := range list {
macro := cjson.Object()
macro.SetItem(c.Str("_Type"), stringField("Macro"))
macro.SetItem(c.Str("Name"), stringField(item.Name))
macro.SetItem(c.Str("Tokens"), MarshalTokenList(item.Tokens))
root.AddItem(macro)
}
return root
}
func MarshalTokenList(list []*ast.Token) *cjson.JSON {
if list == nil {
return cjson.Null()
}
root := cjson.Array()
for _, item := range list {
root.AddItem(MarshalToken(item))
}
return root
}
func MarshalIdentList(list []*ast.Ident) *cjson.JSON {
if list == nil {
return cjson.Null()
}
root := cjson.Array()
for _, item := range list {
root.AddItem(MarshalASTExpr(item))
}
return root
}
func MarshalASTFile(file *ast.File) *cjson.JSON {
root := cjson.Object()
root.SetItem(c.Str("_Type"), stringField("File"))
root.SetItem(c.Str("decls"), MarshalDeclList(file.Decls))
root.SetItem(c.Str("includes"), MarshalIncludeList(file.Includes))
root.SetItem(c.Str("macros"), MarshalMacroList(file.Macros))
return root
}
func MarshalToken(tok *ast.Token) *cjson.JSON {
root := cjson.Object()
root.SetItem(c.Str("_Type"), stringField("Token"))
root.SetItem(c.Str("Token"), numberField(uint(tok.Token)))
root.SetItem(c.Str("Lit"), stringField(tok.Lit))
return root
}
func MarshalASTDecl(decl ast.Decl) *cjson.JSON {
if decl == nil {
return cjson.Null()
}
root := cjson.Object()
switch d := decl.(type) {
case *ast.EnumTypeDecl:
root.SetItem(c.Str("_Type"), stringField("EnumTypeDecl"))
MarshalASTDeclBase(d.DeclBase, root)
root.SetItem(c.Str("Name"), MarshalASTExpr(d.Name))
root.SetItem(c.Str("Type"), MarshalASTExpr(d.Type))
case *ast.TypedefDecl:
root.SetItem(c.Str("_Type"), stringField("TypedefDecl"))
MarshalASTDeclBase(d.DeclBase, root)
root.SetItem(c.Str("Name"), MarshalASTExpr(d.Name))
root.SetItem(c.Str("Type"), MarshalASTExpr(d.Type))
case *ast.FuncDecl:
root.SetItem(c.Str("_Type"), stringField("FuncDecl"))
MarshalASTDeclBase(d.DeclBase, root)
root.SetItem(c.Str("Name"), MarshalASTExpr(d.Name))
root.SetItem(c.Str("MangledName"), stringField(d.MangledName))
root.SetItem(c.Str("Type"), MarshalASTExpr(d.Type))
root.SetItem(c.Str("IsInline"), boolField(d.IsInline))
root.SetItem(c.Str("IsStatic"), boolField(d.IsStatic))
root.SetItem(c.Str("IsConst"), boolField(d.IsConst))
root.SetItem(c.Str("IsExplicit"), boolField(d.IsExplicit))
root.SetItem(c.Str("IsConstructor"), boolField(d.IsConstructor))
root.SetItem(c.Str("IsDestructor"), boolField(d.IsDestructor))
root.SetItem(c.Str("IsVirtual"), boolField(d.IsVirtual))
root.SetItem(c.Str("IsOverride"), boolField(d.IsOverride))
case *ast.TypeDecl:
root.SetItem(c.Str("_Type"), stringField("TypeDecl"))
MarshalASTDeclBase(d.DeclBase, root)
root.SetItem(c.Str("Name"), MarshalASTExpr(d.Name))
root.SetItem(c.Str("Type"), MarshalASTExpr(d.Type))
}
return root
}
func MarshalASTDeclBase(decl ast.DeclBase, root *cjson.JSON) {
loc := cjson.Object()
loc.SetItem(c.Str("_Type"), stringField("Location"))
loc.SetItem(c.Str("File"), stringField(decl.Loc.File))
root.SetItem(c.Str("Loc"), loc)
root.SetItem(c.Str("Doc"), MarshalASTExpr(decl.Doc))
root.SetItem(c.Str("Parent"), MarshalASTExpr(decl.Parent))
}
func MarshalASTExpr(t ast.Expr) *cjson.JSON {
if t == nil {
return cjson.Null()
}
root := cjson.Object()
switch d := t.(type) {
case *ast.EnumType:
root.SetItem(c.Str("_Type"), stringField("EnumType"))
items := cjson.Array()
for _, e := range d.Items {
items.AddItem(MarshalASTExpr(e))
}
root.SetItem(c.Str("Items"), items)
case *ast.EnumItem:
root.SetItem(c.Str("_Type"), stringField("EnumItem"))
root.SetItem(c.Str("Name"), MarshalASTExpr(d.Name))
root.SetItem(c.Str("Value"), MarshalASTExpr(d.Value))
case *ast.RecordType:
root.SetItem(c.Str("_Type"), stringField("RecordType"))
root.SetItem(c.Str("Tag"), numberField(uint(d.Tag)))
root.SetItem(c.Str("Fields"), MarshalASTExpr(d.Fields))
methods := cjson.Array()
for _, m := range d.Methods {
methods.AddItem(MarshalASTDecl(m))
}
root.SetItem(c.Str("Methods"), methods)
case *ast.FuncType:
root.SetItem(c.Str("_Type"), stringField("FuncType"))
root.SetItem(c.Str("Params"), MarshalASTExpr(d.Params))
root.SetItem(c.Str("Ret"), MarshalASTExpr(d.Ret))
case *ast.FieldList:
root.SetItem(c.Str("_Type"), stringField("FieldList"))
root.SetItem(c.Str("List"), MarshalFieldList(d.List))
case *ast.Field:
root.SetItem(c.Str("_Type"), stringField("Field"))
root.SetItem(c.Str("Type"), MarshalASTExpr(d.Type))
root.SetItem(c.Str("Doc"), MarshalASTExpr(d.Doc))
root.SetItem(c.Str("Comment"), MarshalASTExpr(d.Comment))
root.SetItem(c.Str("IsStatic"), boolField(d.IsStatic))
root.SetItem(c.Str("Access"), numberField(uint(d.Access)))
root.SetItem(c.Str("Names"), MarshalIdentList(d.Names))
case *ast.Variadic:
root.SetItem(c.Str("_Type"), stringField("Variadic"))
case *ast.Ident:
root.SetItem(c.Str("_Type"), stringField("Ident"))
if d == nil {
return cjson.Null()
}
root.SetItem(c.Str("Name"), stringField(d.Name))
case *ast.TagExpr:
root.SetItem(c.Str("_Type"), stringField("TagExpr"))
root.SetItem(c.Str("Name"), MarshalASTExpr(d.Name))
root.SetItem(c.Str("Tag"), numberField(uint(d.Tag)))
case *ast.BasicLit:
root.SetItem(c.Str("_Type"), stringField("BasicLit"))
root.SetItem(c.Str("Kind"), numberField(uint(d.Kind)))
root.SetItem(c.Str("Value"), stringField(d.Value))
case *ast.LvalueRefType:
root.SetItem(c.Str("_Type"), stringField("LvalueRefType"))
root.SetItem(c.Str("X"), MarshalASTExpr(d.X))
case *ast.RvalueRefType:
root.SetItem(c.Str("_Type"), stringField("RvalueRefType"))
root.SetItem(c.Str("X"), MarshalASTExpr(d.X))
case *ast.PointerType:
root.SetItem(c.Str("_Type"), stringField("PointerType"))
root.SetItem(c.Str("X"), MarshalASTExpr(d.X))
case *ast.ArrayType:
root.SetItem(c.Str("_Type"), stringField("ArrayType"))
root.SetItem(c.Str("Elt"), MarshalASTExpr(d.Elt))
root.SetItem(c.Str("Len"), MarshalASTExpr(d.Len))
case *ast.BuiltinType:
root.SetItem(c.Str("_Type"), stringField("BuiltinType"))
root.SetItem(c.Str("Kind"), numberField(uint(d.Kind)))
root.SetItem(c.Str("Flags"), numberField(uint(d.Flags)))
case *ast.Comment:
root.SetItem(c.Str("_Type"), stringField("Comment"))
if d == nil {
return cjson.Null()
}
root.SetItem(c.Str("Text"), stringField(d.Text))
case *ast.CommentGroup:
root.SetItem(c.Str("_Type"), stringField("CommentGroup"))
if d == nil {
return cjson.Null()
}
list := cjson.Array()
for _, c := range d.List {
list.AddItem(MarshalASTExpr(c))
}
root.SetItem(c.Str("List"), list)
case *ast.ScopingExpr:
root.SetItem(c.Str("_Type"), stringField("ScopingExpr"))
root.SetItem(c.Str("X"), MarshalASTExpr(d.X))
root.SetItem(c.Str("Parent"), MarshalASTExpr(d.Parent))
default:
return cjson.Null()
}
return root
}
func stringField(s string) *cjson.JSON {
return cjson.String(c.AllocaCStr(s))
}
func numberField(n uint) *cjson.JSON {
return cjson.Number(float64(n))
}
func boolField(b bool) *cjson.JSON {
if b {
return cjson.True()
}
return cjson.False()
}

View File

@@ -0,0 +1,68 @@
package parse
import (
"errors"
"github.com/goplus/llgo/c/cjson"
)
type Context struct {
Files []*FileEntry
IsCpp bool
}
func NewContext(isCpp bool) *Context {
return &Context{
Files: make([]*FileEntry, 0),
IsCpp: isCpp,
}
}
func (p *Context) Output() *cjson.JSON {
return MarshalOutputASTFiles(p.Files)
}
// ProcessFiles processes the given files and adds them to the context
func (p *Context) ProcessFiles(files []string) error {
for _, file := range files {
if err := p.processFile(file); err != nil {
return err
}
}
return nil
}
// parse file and add it to the context,avoid duplicate parsing
func (p *Context) processFile(path string) error {
for _, entry := range p.Files {
if entry.Path == path {
return nil
}
}
parsedFiles, err := p.parseFile(path)
if err != nil {
return errors.New("failed to parse file: " + path)
}
p.Files = append(p.Files, parsedFiles...)
return nil
}
func (p *Context) parseFile(path string) ([]*FileEntry, error) {
converter, err := NewConverter(&Config{
File: path,
Temp: false,
IsCpp: p.IsCpp,
})
if err != nil {
return nil, errors.New("failed to create converter " + path)
}
defer converter.Dispose()
files, err := converter.Convert()
if err != nil {
return nil, err
}
return files, nil
}