Merge pull request #888 from xushiwei/q

mv llcppg => github.com/goplus/llcppg
This commit is contained in:
xushiwei
2024-11-27 17:35:50 +08:00
committed by GitHub
55 changed files with 0 additions and 9691 deletions

View File

@@ -101,9 +101,6 @@ jobs:
- name: Install
run: go install ./...
- name: Install llcppg
run: bash .github/workflows/install_llcppg_depend.sh
- name: Test
if: ${{!startsWith(matrix.os, 'macos')}}
run: go test -v ./...

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -e
llgo install ./chore/_xtool/llcppsymg
llgo install ./chore/_xtool/llcppsigfetch
go install ./chore/gogensig

View File

@@ -1,273 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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/args"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
)
func main() {
ags, remainArgs := args.ParseArgs(os.Args[1:], map[string]bool{
"--extract": true,
})
if ags.Help {
printUsage()
return
}
if ags.Verbose {
parse.SetDebug(parse.DbgFlagAll)
}
extract := false
out := false
var extractFile string
isTemp := false
isCpp := true
otherArgs := []string{}
for i := 0; i < len(remainArgs); i++ {
arg := remainArgs[i]
switch {
case arg == "--extract":
extract = true
if i+1 < len(remainArgs) && !strings.HasPrefix(remainArgs[i+1], "-") {
extractFile = remainArgs[i+1]
i++
} else {
fmt.Fprintln(os.Stderr, "Error: --extract requires a valid file argument")
printUsage()
os.Exit(1)
}
case strings.HasPrefix(arg, "-out="):
out = parseBoolArg(arg, "out", false)
case strings.HasPrefix(arg, "-temp="):
isTemp = parseBoolArg(arg, "temp", false)
case strings.HasPrefix(arg, "-cpp="):
isCpp = parseBoolArg(arg, "cpp", true)
default:
otherArgs = append(otherArgs, arg)
}
}
if extract {
if ags.Verbose {
fmt.Fprintln(os.Stderr, "runExtract: extractFile:", extractFile)
fmt.Fprintln(os.Stderr, "isTemp:", isTemp)
fmt.Fprintln(os.Stderr, "isCpp:", isCpp)
fmt.Fprintln(os.Stderr, "out:", out)
fmt.Fprintln(os.Stderr, "otherArgs:", otherArgs)
}
runExtract(extractFile, isTemp, isCpp, out, otherArgs, ags.Verbose)
} else {
if ags.Verbose {
fmt.Fprintln(os.Stderr, "runFromConfig: config file:", ags.CfgFile)
fmt.Fprintln(os.Stderr, "use stdin:", ags.UseStdin)
fmt.Fprintln(os.Stderr, "output to file:", out)
}
runFromConfig(ags.CfgFile, ags.UseStdin, out, ags.Verbose)
}
}
func printUsage() {
fmt.Println("Usage:")
fmt.Println(" llcppsigfetch [<config_file>] [-out=<bool>]")
fmt.Println(" OR")
fmt.Println(" llcppsigfetch --extract <file> [-out=<bool>] [-temp=<bool>] [-cpp=<bool>] [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(" -out=<bool>: Optional. Set to 'true' to output results to a file,")
fmt.Println(" 'false' (default) to output to stdout")
fmt.Println(" This option can be used with both modes")
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 string, useStdin bool, outputToFile bool, verbose bool) {
var data []byte
var err error
if useStdin {
data, err = io.ReadAll(os.Stdin)
} else {
data, err = os.ReadFile(cfgFile)
}
if verbose {
if useStdin {
fmt.Fprintln(os.Stderr, "runFromConfig: read from stdin")
} else {
fmt.Fprintln(os.Stderr, "runFromConfig: read from file", cfgFile)
}
}
check(err)
conf, err := config.GetConf(data)
check(err)
defer conf.Delete()
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile)
os.Exit(1)
}
//todo(zzy): reuse the llcppsymg's cflags parse
cflag := ParseCFlags(conf.CFlags)
files, notFounds, err := cflag.GenHeaderFilePaths(conf.Include)
check(err)
if verbose {
fmt.Fprintln(os.Stderr, "runFromConfig: header file paths", files)
if len(notFounds) > 0 {
fmt.Fprintln(os.Stderr, "runFromConfig: not found header files", notFounds)
}
}
context := parse.NewContext(conf.Cplusplus)
err = context.ProcessFiles(files)
check(err)
outputInfo(context, outputToFile)
}
func runExtract(file string, isTemp bool, isCpp bool, outToFile bool, otherArgs []string, verbose bool) {
cfg := &clangutils.Config{
File: file,
Args: otherArgs,
IsCpp: isCpp,
Temp: isTemp,
}
converter, err := parse.NewConverter(cfg)
check(err)
_, err = converter.Convert()
check(err)
result := converter.MarshalOutputASTFiles()
cstr := result.Print()
outputResult(cstr, outToFile)
cjson.FreeCStr(cstr)
result.Delete()
converter.Dispose()
}
func check(err error) {
if err != nil {
panic(err)
}
}
func outputResult(result *c.Char, outputToFile bool) {
if outputToFile {
outputFile := "llcppg.sigfetch.json"
err := os.WriteFile(outputFile, []byte(c.GoString(result)), 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing to output file: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Results saved to %s\n", outputFile)
} else {
c.Printf(c.Str("%s"), result)
}
}
// todo(zzy): reuse the llcppsymg's cflags parse https://github.com/goplus/llgo/pull/788
type CFlags struct {
Paths []string // Include Path
}
func ParseCFlags(cflags string) *CFlags {
parts := strings.Fields(cflags)
cf := &CFlags{}
for _, part := range parts {
if strings.HasPrefix(part, "-I") {
cf.Paths = append(cf.Paths, part[2:])
}
}
return cf
}
func (cf *CFlags) GenHeaderFilePaths(files []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
for _, file := range files {
var found bool
for _, path := range cf.Paths {
fullPath := filepath.Join(path, file)
if _, err := os.Stat(fullPath); err == nil {
foundPaths = append(foundPaths, fullPath)
found = true
break
}
}
if !found {
notFound = append(notFound, file)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any header files")
}
return foundPaths, notFound, nil
}
func outputInfo(context *parse.Context, outputToFile bool) {
info := context.Output()
str := info.Print()
defer cjson.FreeCStr(str)
defer info.Delete()
outputResult(str, outputToFile)
}
func parseBoolArg(arg, name string, defaultValue bool) bool {
parts := strings.SplitN(arg, "=", 2)
if len(parts) != 2 {
fmt.Fprintf(os.Stderr, "Warning: Invalid -%s= argument, defaulting to %v\n", name, defaultValue)
return defaultValue
}
value, err := strconv.ParseBool(parts[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: Invalid -%s= value '%s', defaulting to %v\n", name, parts[1], defaultValue)
return defaultValue
}
return value
}

View File

@@ -1,976 +0,0 @@
package parse
import (
"fmt"
"os"
"runtime"
"strings"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
"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
indent int // for verbose debug
}
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 *clangutils.Config) (*Converter, error) {
if debugParse {
fmt.Fprintln(os.Stderr, "NewConverter: config")
fmt.Fprintln(os.Stderr, "config.File", config.File)
fmt.Fprintln(os.Stderr, "config.Args", config.Args)
fmt.Fprintln(os.Stderr, "config.IsCpp", config.IsCpp)
fmt.Fprintln(os.Stderr, "config.Temp", config.Temp)
}
index, unit, err := clangutils.CreateTranslationUnit(config)
if err != nil {
return nil, err
}
return &Converter{
Files: make([]*FileEntry, 0),
index: index,
unit: unit,
}, nil
}
func (ct *Converter) Dispose() {
ct.logln("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) logBase() string {
return strings.Repeat(" ", ct.indent)
}
func (ct *Converter) incIndent() {
ct.indent++
}
func (ct *Converter) decIndent() {
if ct.indent > 0 {
ct.indent--
}
}
func (ct *Converter) logf(format string, args ...interface{}) {
if debugParse {
fmt.Fprintf(os.Stderr, ct.logBase()+format, args...)
}
}
func (ct *Converter) logln(args ...interface{}) {
if debugParse {
if len(args) > 0 {
firstArg := fmt.Sprintf("%s%v", ct.logBase(), args[0])
fmt.Fprintln(os.Stderr, append([]interface{}{firstArg}, args[1:]...)...)
} else {
fmt.Fprintln(os.Stderr, ct.logBase())
}
}
}
func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
loc := cursor.Location()
var file clang.File
loc.SpellingLocation(&file, nil, nil, nil)
filePath := toStr(file.FileName())
if filePath == "" {
//todo(zzy): For some built-in macros, there is no file.
ct.curLoc = ast.Location{File: ""}
return
}
ct.curLoc = ast.Location{File: filePath}
}
func (ct *Converter) GetCurFile() *ast.File {
if ct.curLoc.File == "" {
ct.logln("GetCurFile: NO FILE")
return nil
}
// todo(zzy): more efficient
for i, entry := range ct.Files {
if entry.Path == ct.curLoc.File {
ct.logln("GetCurFile: found", ct.curLoc.File)
return ct.Files[i].Doc
}
}
ct.logln("GetCurFile: Create New ast.File", ct.curLoc.File)
newDoc := &ast.File{}
ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc})
return newDoc
}
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 := toStr(cursor.RawCommentText())
commentGroup := &ast.CommentGroup{}
if rawComment != "" {
commentRange := cursor.CommentRange()
cursorRange := cursor.Extent()
isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart())
commentGroup = ct.ParseComment(rawComment)
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 + "\n"})
}
return commentGroup
}
// visit top decls (struct,class,function,enum & macro,include)
func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResult {
ct.incIndent()
defer ct.decIndent()
ct.UpdateLoc(cursor)
curFile := ct.GetCurFile()
name := toStr(cursor.String())
ct.logf("visitTop: Cursor: %s\n", name)
if curFile == nil {
return clang.ChildVisit_Continue
}
switch cursor.Kind {
case clang.CursorInclusionDirective:
include := ct.ProcessInclude(cursor)
curFile.Includes = append(curFile.Includes, include)
ct.logln("visitTop: ProcessInclude END ", include.Path)
case clang.CursorMacroDefinition:
macro := ct.ProcessMacro(cursor)
curFile.Macros = append(curFile.Macros, macro)
ct.logln("visitTop: ProcessMacro END ", macro.Name, "Tokens Length:", len(macro.Tokens))
case clang.CursorEnumDecl:
enum := ct.ProcessEnumDecl(cursor)
curFile.Decls = append(curFile.Decls, enum)
ct.logf("visitTop: ProcessEnumDecl END")
if enum.Name != nil {
ct.logln(enum.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorClassDecl:
classDecl := ct.ProcessClassDecl(cursor)
curFile.Decls = append(curFile.Decls, classDecl)
// class havent anonymous situation
ct.logln("visitTop: ProcessClassDecl END", classDecl.Name.Name)
case clang.CursorStructDecl:
structDecl := ct.ProcessStructDecl(cursor)
curFile.Decls = append(curFile.Decls, structDecl)
ct.logf("visitTop: ProcessStructDecl END")
if structDecl.Name != nil {
ct.logln(structDecl.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorUnionDecl:
unionDecl := ct.ProcessUnionDecl(cursor)
curFile.Decls = append(curFile.Decls, unionDecl)
ct.logf("visitTop: ProcessUnionDecl END")
if unionDecl.Name != nil {
ct.logln(unionDecl.Name.Name)
} else {
ct.logln("ANONY")
}
case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
// Handle functions and class methods (including out-of-class method)
// Example: void MyClass::myMethod() { ... } out-of-class method
funcDecl := ct.ProcessFuncDecl(cursor)
curFile.Decls = append(curFile.Decls, funcDecl)
ct.logln("visitTop: ProcessFuncDecl END", funcDecl.Name.Name, funcDecl.MangledName, "isStatic:", funcDecl.IsStatic, "isInline:", funcDecl.IsInline)
case clang.CursorTypedefDecl:
typedefDecl := ct.ProcessTypeDefDecl(cursor)
if typedefDecl == nil {
return clang.ChildVisit_Continue
}
curFile.Decls = append(curFile.Decls, typedefDecl)
ct.logln("visitTop: ProcessTypeDefDecl END", typedefDecl.Name.Name)
case clang.CursorNamespace:
VisitChildren(cursor, ct.visitTop)
}
return clang.ChildVisit_Continue
}
func (ct *Converter) Convert() ([]*FileEntry, error) {
cursor := ct.unit.Cursor()
// visit top decls (struct,class,function & macro,include)
VisitChildren(cursor, ct.visitTop)
return ct.Files, nil
}
type Visitor func(cursor, parent clang.Cursor) clang.ChildVisitResult
func VisitChildren(cursor clang.Cursor, fn Visitor) c.Uint {
return clang.VisitChildren(cursor, func(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
cfn := *(*Visitor)(clientData)
return cfn(cursor, parent)
}, unsafe.Pointer(&fn))
}
func (ct *Converter) ProcessType(t clang.Type) ast.Expr {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessType: TypeName:", typeName, "TypeKind:", typeKind)
if t.Kind >= clang.TypeFirstBuiltin && t.Kind <= clang.TypeLastBuiltin {
return ct.ProcessBuiltinType(t)
}
if t.Kind == clang.TypeElaborated {
return ct.ProcessElaboratedType(t)
}
if t.Kind == clang.TypeTypedef {
return ct.ProcessTypeDefType(t)
}
var expr ast.Expr
switch t.Kind {
case clang.TypePointer:
name, kind := getTypeDesc(t.PointeeType())
ct.logln("ProcessType: PointerType Pointee TypeName:", name, "TypeKind:", kind)
expr = &ast.PointerType{X: ct.ProcessType(t.PointeeType())}
case clang.TypeLValueReference:
name, kind := getTypeDesc(t.NonReferenceType())
ct.logln("ProcessType: LvalueRefType NonReference TypeName:", name, "TypeKind:", kind)
expr = &ast.LvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
case clang.TypeRValueReference:
name, kind := getTypeDesc(t.NonReferenceType())
ct.logln("ProcessType: RvalueRefType NonReference TypeName:", name, "TypeKind:", kind)
expr = &ast.RvalueRefType{X: ct.ProcessType(t.NonReferenceType())}
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
name, kind := getTypeDesc(t)
ct.logln("ProcessType: FunctionType TypeName:", name, "TypeKind:", kind)
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()),
}
}
default:
name, kind := getTypeDesc(t)
ct.logln("ProcessType: Unknown Type TypeName:", name, "TypeKind:", kind)
}
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 {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessFunctionType: TypeName:", typeName, "TypeKind:", typeKind)
// Note: Attempting to get the type declaration for a function type will result in CursorNoDeclFound
// cursor := t.TypeDeclaration()
// This would return CursorNoDeclFound
resType := t.ResultType()
name, kind := getTypeDesc(resType)
ct.logln("ProcessFunctionType: ResultType TypeName:", name, "TypeKind:", kind)
ret := ct.ProcessType(resType)
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 {
ct.incIndent()
defer ct.decIndent()
name, kind := getCursorDesc(cursor)
ct.logln("ProcessTypeDefDecl: CursorName:", name, "CursorKind:", kind, "CursorTypeKind:", toStr(cursor.Type().Kind.String()))
typ := ct.ProcessUnderlyingType(cursor)
// For cases like: typedef struct { int x; } Name;
// libclang incorrectly reports the anonymous structure as a named structure
// with the same name as the typedef. Since the anonymous structure definition
// has already been collected when processing its declaration cursor,
// we skip this redundant typedef declaration by returning nil.
if typ == nil {
return nil
}
decl := &ast.TypedefDecl{
DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: name},
Type: typ,
}
return decl
}
func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
underlyingTyp := cursor.TypedefDeclUnderlyingType()
if underlyingTyp.Kind != clang.TypeElaborated {
ct.logln("ProcessUnderlyingType: not elaborated")
return ct.ProcessType(underlyingTyp)
}
referTypeCursor := underlyingTyp.TypeDeclaration()
if toStr(cursor.String()) == toStr(referTypeCursor.String()) && isCursorChildOf(referTypeCursor, cursor) {
ct.logln("ProcessUnderlyingType: is self reference")
return nil
}
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 {
ct.incIndent()
defer ct.decIndent()
name, kind := getCursorDesc(cursor)
mangledName := toStr(cursor.Mangling())
ct.logln("ProcessFuncDecl: CursorName:", name, "CursorKind:", kind)
// 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 {
ct.logln("ProcessFuncDecl: failed to process function type")
return nil
}
ct.logln("ProcessFuncDecl: ProcessFieldList")
params := ct.ProcessFieldList(cursor)
funcType.Params = params
// Linux has one less leading underscore than macOS, so remove one leading underscore on macOS
if runtime.GOOS == "darwin" {
mangledName = strings.TrimPrefix(mangledName, "_")
}
funcDecl := &ast.FuncDecl{
DeclBase: ct.CreateDeclBase(cursor),
Name: &ast.Ident{Name: name},
Type: funcType,
MangledName: mangledName,
}
if cursor.IsFunctionInlined() != 0 {
funcDecl.IsInline = true
}
if isMethod(cursor) {
ct.logln("ProcessFuncDecl: is method, ProcessMethodAttributes")
ct.ProcessMethodAttributes(cursor, funcDecl)
} else {
if cursor.StorageClass() == clang.SCStatic {
funcDecl.IsStatic = true
}
}
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()
}
func (ct *Converter) ProcessEnumType(cursor clang.Cursor) *ast.EnumType {
items := make([]*ast.EnumItem, 0)
VisitChildren(cursor, func(cursor, parent clang.Cursor) clang.ChildVisitResult {
if cursor.Kind == clang.CursorEnumConstantDecl {
name := cursor.String()
defer name.Dispose()
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))
enum := &ast.EnumItem{
Name: &ast.Ident{Name: c.GoString(name.CStr())},
Value: &ast.BasicLit{
Kind: ast.IntLit,
Value: c.GoString(val),
},
}
items = append(items, enum)
}
return clang.ChildVisit_Continue
})
return &ast.EnumType{
Items: items,
}
}
func (ct *Converter) ProcessEnumDecl(cursor clang.Cursor) *ast.EnumTypeDecl {
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessEnumDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
decl := &ast.EnumTypeDecl{
DeclBase: ct.CreateDeclBase(cursor),
Type: ct.ProcessEnumType(cursor),
}
anony := cursor.IsAnonymous()
if anony == 0 {
decl.Name = &ast.Ident{Name: cursorName}
ct.logln("ProcessEnumDecl: has name", cursorName)
} else {
ct.logln("ProcessRecordDecl: is anonymous")
}
return decl
}
// current only collect macro which defined in file
func (ct *Converter) ProcessMacro(cursor clang.Cursor) *ast.Macro {
name := toStr(cursor.String())
macro := &ast.Macro{
Name: name,
Tokens: ct.GetTokens(cursor),
}
return macro
}
func (ct *Converter) ProcessInclude(cursor clang.Cursor) *ast.Include {
name := toStr(cursor.String())
return &ast.Include{Path: name}
}
func (ct *Converter) createBaseField(cursor clang.Cursor) *ast.Field {
ct.incIndent()
defer ct.decIndent()
fieldName := toStr(cursor.String())
typ := cursor.Type()
typeName, typeKind := getTypeDesc(typ)
ct.logf("createBaseField: ProcessType %s TypeKind: %s", typeName, typeKind)
field := &ast.Field{
Type: ct.ProcessType(typ),
}
commentGroup, isDoc := ct.ParseCommentGroup(cursor)
if commentGroup != nil {
if isDoc {
field.Doc = commentGroup
} else {
field.Comment = commentGroup
}
}
if fieldName != "" {
field.Names = []*ast.Ident{{Name: fieldName}}
}
return field
}
// For Record Type(struct,union ...) & Func 's FieldList
func (ct *Converter) ProcessFieldList(cursor clang.Cursor) *ast.FieldList {
ct.incIndent()
defer ct.decIndent()
params := &ast.FieldList{}
ct.logln("ProcessFieldList: VisitChildren")
VisitChildren(cursor, func(subcsr, parent clang.Cursor) clang.ChildVisitResult {
switch subcsr.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;
// };
if subcsr.Kind == clang.CursorFieldDecl {
ct.logln("ProcessFieldList: CursorFieldDecl")
} else {
ct.logln("ProcessFieldList: CursorParmDecl")
}
field := ct.createBaseField(subcsr)
if subcsr.Kind == clang.CursorFieldDecl {
field.Access = ast.AccessSpecifier(subcsr.CXXAccessSpecifier())
}
params.List = append(params.List, field)
case clang.CursorVarDecl:
if subcsr.StorageClass() == clang.SCStatic {
// static member variable
field := ct.createBaseField(subcsr)
field.Access = ast.AccessSpecifier(subcsr.CXXAccessSpecifier())
field.IsStatic = true
params.List = append(params.List, field)
}
}
return clang.ChildVisit_Continue
})
if (cursor.Kind == clang.CursorFunctionDecl || isMethod(cursor)) && cursor.IsVariadic() != 0 {
params.List = append(params.List, &ast.Field{
Type: &ast.Variadic{},
})
}
return params
}
// Note:Public Method is considered
func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
methods := make([]*ast.FuncDecl, 0)
VisitChildren(cursor, func(subcsr, parent clang.Cursor) clang.ChildVisitResult {
if isMethod(subcsr) && subcsr.CXXAccessSpecifier() == clang.CXXPublic {
method := ct.ProcessFuncDecl(subcsr)
if method != nil {
methods = append(methods, method)
}
}
return clang.ChildVisit_Continue
})
return methods
}
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
ct.incIndent()
defer ct.decIndent()
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessRecordDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
decl := &ast.TypeDecl{
DeclBase: ct.CreateDeclBase(cursor),
Type: ct.ProcessRecordType(cursor),
}
anony := cursor.IsAnonymousRecordDecl()
if anony == 0 {
decl.Name = &ast.Ident{Name: cursorName}
ct.logln("ProcessRecordDecl: has name", cursorName)
} else {
ct.logln("ProcessRecordDecl: is anonymous")
}
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 {
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessClassDecl: CursorName:", cursorName, "CursorKind:", cursorKind)
// 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: cursorName},
Type: typ,
}
return decl
}
func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
ct.incIndent()
defer ct.decIndent()
cursorName, cursorKind := getCursorDesc(cursor)
ct.logln("ProcessRecordType: CursorName:", cursorName, "CursorKind:", cursorKind)
tag := toTag(cursor.Kind)
ct.logln("ProcessRecordType: toTag", tag)
ct.logln("ProcessRecordType: ProcessFieldList")
fields := ct.ProcessFieldList(cursor)
ct.logln("ProcessRecordType: ProcessMethods")
methods := ct.ProcessMethods(cursor)
return &ast.RecordType{
Tag: tag,
Fields: fields,
Methods: methods,
}
}
// 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 {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessElaboratedType: TypeName:", typeName, "TypeKind:", typeKind)
decl := t.TypeDeclaration()
if decl.IsAnonymous() != 0 {
// anonymous type refer (except anonymous RecordType&EnumType in TypedefDecl)
if decl.Kind == clang.CursorEnumDecl {
return ct.ProcessEnumType(decl)
}
return ct.ProcessRecordType(decl)
}
// 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) ProcessTypeDefType(t clang.Type) ast.Expr {
cursor := t.TypeDeclaration()
ct.logln("ProcessTypeDefType: Typedef TypeDeclaration", toStr(cursor.String()), toStr(t.String()))
if name := toStr(cursor.String()); name != "" {
return &ast.Ident{Name: name}
}
ct.logln("ProcessTypeDefType: typedef type have no name")
return nil
}
func (ct *Converter) ProcessBuiltinType(t clang.Type) *ast.BuiltinType {
ct.incIndent()
defer ct.decIndent()
typeName, typeKind := getTypeDesc(t)
ct.logln("ProcessBuiltinType: TypeName:", typeName, "TypeKind:", typeKind)
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 := toStr(t.Kind.String())
fmt.Fprintln(os.Stderr, "todo: unknown builtin type:", kindStr)
}
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 := clangutils.BuildScopingParts(cursor)
return buildScopingFromParts(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
}
func toStr(clangStr clang.String) (str string) {
defer clangStr.Dispose()
if clangStr.CStr() != nil {
str = c.GoString(clangStr.CStr())
}
return
}
func getTypeDesc(t clang.Type) (name string, kind string) {
name = toStr(t.String())
kind = toStr(t.Kind.String())
return
}
func getCursorDesc(cursor clang.Cursor) (name string, kind string) {
name = toStr(cursor.String())
kind = toStr(cursor.Kind.String())
return
}

View File

@@ -1,90 +0,0 @@
package cvttest
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"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
)
func RunTest(testName string, testCases []string) {
for i, content := range testCases {
c.Printf(c.Str("%s Case %d:\n"), c.AllocaCStr(testName), c.Int(i+1))
RunTestWithConfig(&clangutils.Config{
File: content,
Temp: true,
IsCpp: true,
})
}
}
func RunTestWithConfig(config *clangutils.Config) {
converter, err := parse.NewConverter(config)
if err != nil {
panic(err)
}
_, err = converter.Convert()
if err != nil {
panic(err)
}
result := converter.MarshalASTFiles()
str := result.Print()
c.Printf(c.Str("%s\n\n"), 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 := clangutils.CreateTranslationUnit(&clangutils.Config{
File: code,
Temp: true,
Args: option.Args,
IsCpp: option.IsCpp,
})
if err != nil {
panic(err)
}
cursor := unit.Cursor()
var typ clang.Type
parse.VisitChildren(cursor, func(child, parent clang.Cursor) clang.ChildVisitResult {
if child.Kind == clang.CursorVarDecl && (option.ExpectTypeKind == clang.TypeInvalid || option.ExpectTypeKind == child.Type().Kind) {
typ = child.Type()
return clang.ChildVisit_Break
}
return clang.ChildVisit_Continue
})
return typ, index, unit
}

View File

@@ -1,53 +0,0 @@
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

@@ -1,713 +0,0 @@
#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

@@ -1,78 +0,0 @@
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

@@ -1,742 +0,0 @@
#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\n"
}]
},
"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 */\n"
}]
},
"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 */\n"
}]
},
"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\n"
}, {
"_Type": "Comment",
"Text": "/// doc 2\n"
}]
},
"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 */\n"
}, {
"_Type": "Comment",
"Text": "/*! doc 2 */\n"
}]
},
"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 */\n"
}, {
"_Type": "Comment",
"Text": "/** doc 1 */\n"
}]
},
"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": "/**\n"
}, {
"_Type": "Comment",
"Text": " * doc 1\n"
}, {
"_Type": "Comment",
"Text": " * doc 2\n"
}, {
"_Type": "Comment",
"Text": " */\n"
}]
},
"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\n"
}]
},
"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\n"
}]
},
"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 */\n"
}]
},
"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": "/** \n"
}, {
"_Type": "Comment",
"Text": " * static field doc\n"
}, {
"_Type": "Comment",
"Text": " */\n"
}]
},
"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 */\n"
}]
},
"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": "/** \n"
}, {
"_Type": "Comment",
"Text": " * field doc\n"
}, {
"_Type": "Comment",
"Text": " */\n"
}]
},
"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\n"
}]
},
"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 */\n"
}]
},
"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": "/** \n"
}, {
"_Type": "Comment",
"Text": " * method doc\n"
}, {
"_Type": "Comment",
"Text": " */\n"
}]
},
"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

@@ -1,33 +0,0 @@
package main
import test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
func main() {
TestEnumDecl()
}
func TestEnumDecl() {
testCases := []string{
`enum {
a,
b,
c,
};`,
`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

@@ -1,238 +0,0 @@
#stdout
TestEnumDecl Case 1:
{
"temp.h": {
"_Type": "File",
"decls": [{
"_Type": "EnumTypeDecl",
"Loc": {
"_Type": "Location",
"File": "temp.h"
},
"Doc": null,
"Parent": null,
"Name": null,
"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": "0"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "b"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "1"
}
}, {
"_Type": "EnumItem",
"Name": {
"_Type": "Ident",
"Name": "c"
},
"Value": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "2"
}
}]
}
}],
"includes": [],
"macros": []
}
}
TestEnumDecl Case 3:
{
"temp.h": {
"_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 4:
{
"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

@@ -1,18 +0,0 @@
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

@@ -1,323 +0,0 @@
#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

@@ -1,291 +0,0 @@
#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

@@ -1,32 +0,0 @@
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

@@ -1,375 +0,0 @@
#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

@@ -1,35 +0,0 @@
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)
}

View File

@@ -1,59 +0,0 @@
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[];`,
`typedef enum {
RED,
GREEN,
BLUE
} MyEnum,MyEnum2,*EnumPtr,EnumArr[];`,
`namespace A{
namespace B{
typedef struct {
int x;
} MyStruct,MyStruct2,*StructPtr, StructArr[];
}
}`,
}
test.RunTest("TestTypeDefDecl", testCases)
}

View File

@@ -1,234 +0,0 @@
#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": 6,
"Flags": 0
},
"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

@@ -1,29 +0,0 @@
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 {
int c;
short s;
} inner;
};`,
}
test.RunTest("TestUnionDecl", testCases)
}

View File

@@ -1,231 +0,0 @@
#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": []
}
}
TestMacroExpansionOtherFile:
{
"./testdata/macroexpan/ref.h": {
"_Type": "File",
"decls": [{
"_Type": "TypeDecl",
"Loc": {
"_Type": "Location",
"File": "./testdata/macroexpan/ref.h"
},
"Doc": null,
"Parent": null,
"Name": {
"_Type": "Ident",
"Name": "NewType"
},
"Type": {
"_Type": "RecordType",
"Tag": 0,
"Fields": {
"_Type": "FieldList",
"List": [{
"_Type": "Field",
"Type": {
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Len": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "2"
}
},
"Doc": null,
"Comment": null,
"IsStatic": false,
"Access": 1,
"Names": [{
"_Type": "Ident",
"Name": "__val"
}]
}]
},
"Methods": []
}
}],
"includes": [{
"_Type": "Include",
"Path": "def.h"
}],
"macros": []
},
"./testdata/macroexpan/def.h": {
"_Type": "File",
"decls": [],
"includes": [],
"macros": [{
"_Type": "Macro",
"Name": "__FSID_T_TYPE",
"Tokens": [{
"_Type": "Token",
"Token": 3,
"Lit": "__FSID_T_TYPE"
}, {
"_Type": "Token",
"Token": 2,
"Lit": "struct"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "{"
}, {
"_Type": "Token",
"Token": 2,
"Lit": "int"
}, {
"_Type": "Token",
"Token": 3,
"Lit": "__val"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "["
}, {
"_Type": "Token",
"Token": 4,
"Lit": "2"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "]"
}, {
"_Type": "Token",
"Token": 1,
"Lit": ";"
}, {
"_Type": "Token",
"Token": 1,
"Lit": "}"
}]
}]
}
}
#stderr
#exit 0

View File

@@ -1,39 +0,0 @@
package main
import (
"github.com/goplus/llgo/c"
test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
)
func main() {
TestDefine()
TestInclude()
TestMacroExpansionOtherFile()
}
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)
}
func TestMacroExpansionOtherFile() {
c.Printf(c.Str("TestMacroExpansionOtherFile:\n"))
test.RunTestWithConfig(&clangutils.Config{
File: "./testdata/macroexpan/ref.h",
Temp: false,
IsCpp: false,
})
}

View File

@@ -1,4 +0,0 @@
#define __FSID_T_TYPE \
struct { \
int __val[2]; \
}

View File

@@ -1,2 +0,0 @@
#include "def.h"
typedef __FSID_T_TYPE NewType;

View File

@@ -1,351 +0,0 @@
#stdout
Char's flags is signed or unsigned
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: int *:
{
"_Type": "PointerType",
"X": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
}
Type: int ***:
{
"_Type": "PointerType",
"X": {
"_Type": "PointerType",
"X": {
"_Type": "PointerType",
"X": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
}
}
}
}
Type: int[]:
{
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Len": null
}
Type: int[10]:
{
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"Len": {
"_Type": "BasicLit",
"Kind": 0,
"Value": "10"
}
}
Type: int[3][4]:
{
"_Type": "ArrayType",
"Elt": {
"_Type": "ArrayType",
"Elt": {
"_Type": "BuiltinType",
"Kind": 6,
"Flags": 0
},
"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, int):
{
"_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
}
}
}
#stderr
todo: unknown builtin type: Ibm128
#exit 0

View File

@@ -1,201 +0,0 @@
package main
import (
"fmt"
"os"
"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() {
TestChar()
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)
}
}
// Char's Default Type in macos is signed char & in linux is unsigned char
// So we only confirm the char's kind is char & flags is unsigned or signed
func TestChar() {
typ, index, transunit := test.GetType(&test.GetTypeOptions{
TypeCode: "char",
IsCpp: false,
})
converter := &parse.Converter{}
expr := converter.ProcessType(typ)
if btType, ok := expr.(*ast.BuiltinType); ok {
if btType.Kind == ast.Char {
if btType.Flags == ast.Signed || btType.Flags == ast.Unsigned {
fmt.Println("Char's flags is signed or unsigned")
} else {
fmt.Fprintf(os.Stderr, "Char's flags is not signed or unsigned")
}
}
} else {
fmt.Fprintf(os.Stderr, "Char's expr is not a builtin type")
}
index.Dispose()
transunit.Dispose()
}
func TestNonBuiltinTypes() {
tests := []string{
"int*",
"int***",
"int[]",
"int[10]",
"int[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, int);`,
}
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

@@ -1,273 +0,0 @@
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

@@ -1,98 +0,0 @@
package parse
import (
"errors"
"fmt"
"os"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
)
type dbgFlags = int
const (
DbgParse dbgFlags = 1 << iota
DbgFlagAll = DbgParse
)
var (
debugParse bool
)
func SetDebug(dbgFlags dbgFlags) {
debugParse = (dbgFlags & DbgParse) != 0
}
type Context struct {
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 {
if debugParse {
fmt.Fprintln(os.Stderr, "ProcessFiles: files", files, "isCpp", p.IsCpp)
}
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 {
if debugParse {
fmt.Fprintln(os.Stderr, "processFile: path", path)
}
for _, entry := range p.Files {
if entry.Path == path {
if debugParse {
fmt.Fprintln(os.Stderr, "processFile: already parsed", 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) {
if debugParse {
fmt.Fprintln(os.Stderr, "parseFile: path", path)
}
converter, err := NewConverter(&clangutils.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
}

View File

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

View File

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

View File

@@ -1,119 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
)
func main() {
TestClangUtil()
}
func TestClangUtil() {
testCases := []struct {
name string
content string
isTemp bool
isCpp bool
}{
{
name: "C Header File",
content: `
int test_function(int a, int b);
void another_function(void);
`,
isTemp: false,
isCpp: false,
},
{
name: "C++ Temp File",
content: `
class TestClass {
public:
void test_method();
static int static_method(float f);
};
namespace TestNamespace {
void namespaced_function();
}
`,
isTemp: true,
isCpp: true,
},
}
for _, tc := range testCases {
fmt.Printf("=== Test Case: %s ===\n", tc.name)
var filePath string
var tempFile *os.File
if tc.isTemp {
filePath = tc.content
} else {
var err error
tempFile, err = os.CreateTemp("", "test_*.h")
if err != nil {
fmt.Printf("Failed to create temporary file: %v\n", err)
continue
}
_, err = tempFile.Write([]byte(tc.content))
if err != nil {
fmt.Printf("Failed to write to temporary file: %v\n", err)
tempFile.Close()
os.Remove(tempFile.Name())
continue
}
tempFile.Close()
filePath = tempFile.Name()
}
config := &clangutils.Config{
File: filePath,
Temp: tc.isTemp,
IsCpp: tc.isCpp,
}
index, unit, err := clangutils.CreateTranslationUnit(config)
if err != nil {
fmt.Printf("CreateTranslationUnit failed: %v\n", err)
continue
}
fmt.Println("CreateTranslationUnit succeeded")
cursor := unit.Cursor()
clangutils.VisitChildren(cursor, func(cursor, parent clang.Cursor) clang.ChildVisitResult {
switch cursor.Kind {
case clang.CursorFunctionDecl, clang.CursorCXXMethod:
funcName := cursor.String()
fmt.Printf("Function/Method: %s\n", c.GoString(funcName.CStr()))
parts := clangutils.BuildScopingParts(cursor)
fmt.Printf("Scoping parts: %v\n", parts)
funcName.Dispose()
case clang.CursorClassDecl:
className := cursor.String()
fmt.Printf("Class: %s\n", c.GoString(className.CStr()))
className.Dispose()
case clang.CursorNamespace:
namespaceName := cursor.String()
fmt.Printf("Namespace: %s\n", c.GoString(namespaceName.CStr()))
namespaceName.Dispose()
}
return clang.ChildVisit_Recurse
})
index.Dispose()
unit.Dispose()
if !tc.isTemp && tempFile != nil {
os.Remove(tempFile.Name())
}
fmt.Println()
}
}

View File

@@ -1,23 +0,0 @@
#stdout
=== Test Case: C Header File ===
CreateTranslationUnit succeeded
Function/Method: test_function
Scoping parts: [test_function]
Function/Method: another_function
Scoping parts: [another_function]
=== Test Case: C++ Temp File ===
CreateTranslationUnit succeeded
Class: TestClass
Function/Method: test_method
Scoping parts: [TestClass test_method]
Function/Method: static_method
Scoping parts: [TestClass static_method]
Namespace: TestNamespace
Function/Method: namespaced_function
Scoping parts: [TestNamespace namespaced_function]
#stderr
#exit 0

View File

@@ -1,332 +0,0 @@
package main
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config/cfgparse"
)
func main() {
TestGetConf()
TestParseLibs()
TestGenDylibPaths()
TestParseCFlags()
TestGenHeaderFilePath()
}
func TestGetConf() {
testCases := []struct {
name string
input string
}{
{
name: "SQLite configuration",
input: `{
"name": "sqlite",
"cflags": "-I/opt/homebrew/opt/sqlite/include",
"include": ["sqlite3.h"],
"libs": "-L/opt/homebrew/opt/sqlite/lib -lsqlite3",
"trimPrefixes": ["sqlite3_"],
"cplusplus": false
}`,
},
{
name: "Lua configuration",
input: `{
"name": "lua",
"cflags": "-I/opt/homebrew/include/lua",
"include": ["lua.h"],
"libs": "-L/opt/homebrew/lib -llua -lm",
"trimPrefixes": ["lua_", "lua_"],
"cplusplus": false
}`,
},
{
name: "Invalid JSON",
input: `{invalid json}`,
},
}
for _, tc := range testCases {
fmt.Printf("=== Test case: %s ===\n", tc.name)
result, err := config.GetConf([]byte(tc.input))
if err != nil {
fmt.Println("Error:", err.Error())
} else {
fmt.Println("Name:", result.Config.Name)
fmt.Println("CFlags:", result.Config.CFlags)
fmt.Println("Libs:", result.Config.Libs)
fmt.Println("Include:", strings.Join(result.Config.Include, ", "))
fmt.Println("TrimPrefixes:", strings.Join(result.Config.TrimPrefixes, ", "))
fmt.Println("Cplusplus:", result.Config.Cplusplus)
}
fmt.Println()
}
}
func TestParseLibs() {
fmt.Println("=== Test ParseLibs ===")
testCases := []struct {
name string
input string
}{
{
name: "Lua library",
input: "-L/opt/homebrew/lib -llua -lm",
},
{
name: "SQLite library",
input: "-L/opt/homebrew/opt/sqlite/lib -lsqlite3",
},
{
name: "INIReader library",
input: "-L/opt/homebrew/Cellar/inih/58/lib -lINIReader",
},
{
name: "Multiple library paths",
input: "-L/opt/homebrew/lib -L/usr/lib -llua",
},
{
name: "No valid library",
input: "-L/opt/homebrew/lib",
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input: %s\n", tc.input)
conf := cfgparse.ParseLibs(tc.input)
fmt.Println("Paths:", conf.Paths)
fmt.Println("Names:", conf.Names)
}
}
func TestGenDylibPaths() {
fmt.Println("=== Test GenDylibPaths ===")
tempDir := os.TempDir()
tempDefaultPath := filepath.Join(tempDir, "symblib")
affix := ".dylib"
if runtime.GOOS == "linux" {
affix = ".so"
}
err := os.MkdirAll(tempDefaultPath, 0755)
if err != nil {
fmt.Printf("Failed to create temp default path: %v\n", err)
return
}
dylib1 := filepath.Join(tempDir, "libsymb1"+affix)
dylib2 := filepath.Join(tempDir, "libsymb2"+affix)
defaultDylib3 := filepath.Join(tempDefaultPath, "libsymb3"+affix)
os.Create(dylib1)
os.Create(dylib2)
os.Create(defaultDylib3)
defer os.Remove(dylib1)
defer os.Remove(dylib2)
defer os.Remove(defaultDylib3)
defer os.Remove(tempDefaultPath)
testCase := []struct {
name string
conf *cfgparse.Libs
defaultPaths []string
want []string
}{
{
name: "existing dylib",
conf: &cfgparse.Libs{
Names: []string{"symb1"},
Paths: []string{tempDir},
},
defaultPaths: []string{},
want: []string{dylib1},
},
{
name: "existing dylibs",
conf: &cfgparse.Libs{
Names: []string{"symb1", "symb2"},
Paths: []string{tempDir},
},
defaultPaths: []string{},
want: []string{dylib1, dylib2},
},
{
name: "existint default paths",
conf: &cfgparse.Libs{
Names: []string{"symb1", "symb3"},
Paths: []string{tempDir},
},
defaultPaths: []string{tempDefaultPath},
want: []string{dylib1, defaultDylib3},
},
{
name: "existint default paths & not found",
conf: &cfgparse.Libs{
Names: []string{"symb1", "symb3", "math"},
Paths: []string{tempDir},
},
defaultPaths: []string{tempDefaultPath},
want: []string{dylib1, defaultDylib3},
},
{
name: "no existing dylib",
conf: &cfgparse.Libs{
Names: []string{"notexist"},
Paths: []string{tempDir},
},
want: []string{},
},
}
for _, tc := range testCase {
fmt.Printf("Test case: %s\n", tc.name)
paths, notFounds, err := tc.conf.GenDylibPaths(tc.defaultPaths)
if len(notFounds) > 0 {
fmt.Println("notFounds", notFounds)
}
if err != nil {
fmt.Printf("Error: %v\n", err)
}
for _, path := range paths {
found := false
for _, wantPath := range tc.want {
if path == wantPath {
found = true
fileName := filepath.Base(path)
if runtime.GOOS == "linux" {
fileName = strings.TrimSuffix(fileName, ".so")
} else {
fileName = strings.TrimSuffix(fileName, ".dylib")
}
fmt.Printf("Path %s is in the expected paths\n", fileName)
break
}
}
if !found {
fmt.Printf("Path %s is not in the expected paths\n", path)
}
}
}
}
func TestParseCFlags() {
fmt.Println("=== Test ParseCFlags ===")
testCases := []struct {
name string
input string
}{
{
name: "Single include path",
input: "-I/usr/include",
},
{
name: "Multiple include paths",
input: "-I/usr/include -I/opt/homebrew/include",
},
{
name: "Include paths mixed with other flags",
input: "-I/usr/include -DDEBUG -I/opt/local/include -Wall",
},
{
name: "Empty input",
input: "",
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input: %s\n", tc.input)
conf := cfgparse.ParseCFlags(tc.input)
fmt.Println("Paths:", conf.Paths)
}
}
func TestGenHeaderFilePath() {
fmt.Println("=== Test GenHeaderFilePath ===")
tempDir := os.TempDir()
temDir2 := filepath.Join(tempDir, "include")
tempFile1 := filepath.Join(tempDir, "test1.h")
tempFile2 := filepath.Join(tempDir, "test2.h")
tempFile3 := filepath.Join(temDir2, "test3.h")
os.MkdirAll(temDir2, 0755)
os.Create(tempFile1)
os.Create(tempFile2)
os.Create(tempFile3)
defer os.Remove(tempFile1)
defer os.Remove(tempFile2)
defer os.Remove(tempFile3)
defer os.Remove(temDir2)
testCases := []struct {
name string
cflags string
files []string
}{
{
name: "Valid files",
cflags: "-I" + tempDir,
files: []string{"test1.h", "test2.h"},
},
{
name: "Mixed existing and non-existing files",
cflags: "-I" + tempDir,
files: []string{"test1.h", "nonexistent.h"},
},
{
name: "Multiple include paths",
cflags: "-I" + tempDir + " -I" + temDir2,
files: []string{"test1.h", "test2.h", "test3.h"},
},
{
name: "No existing files",
cflags: "-I" + tempDir,
files: []string{"nonexistent1.h", "nonexistent2.h"},
},
{
name: "Empty file list",
cflags: "-I/usr/include",
files: []string{},
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input files: %v\n", tc.files)
cflag := cfgparse.ParseCFlags(tc.cflags)
result, notFounds, err := cflag.GenHeaderFilePaths(tc.files)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
if len(notFounds) > 0 {
fmt.Println("notFounds", notFounds)
}
if result != nil {
relativeResult := make([]string, len(result))
for i, path := range result {
relativeResult[i] = filepath.Base(path)
}
fmt.Printf("Output: %v\n", relativeResult)
}
fmt.Println()
}
}

View File

@@ -1,97 +0,0 @@
#stdout
=== Test case: SQLite configuration ===
Name: sqlite
CFlags: -I/opt/homebrew/opt/sqlite/include
Libs: -L/opt/homebrew/opt/sqlite/lib -lsqlite3
Include: sqlite3.h
TrimPrefixes: sqlite3_
Cplusplus: false
=== Test case: Lua configuration ===
Name: lua
CFlags: -I/opt/homebrew/include/lua
Libs: -L/opt/homebrew/lib -llua -lm
Include: lua.h
TrimPrefixes: lua_, lua_
Cplusplus: false
=== Test case: Invalid JSON ===
Error: failed to parse config
=== Test ParseLibs ===
Test case: Lua library
Input: -L/opt/homebrew/lib -llua -lm
Paths: [/opt/homebrew/lib]
Names: [lua m]
Test case: SQLite library
Input: -L/opt/homebrew/opt/sqlite/lib -lsqlite3
Paths: [/opt/homebrew/opt/sqlite/lib]
Names: [sqlite3]
Test case: INIReader library
Input: -L/opt/homebrew/Cellar/inih/58/lib -lINIReader
Paths: [/opt/homebrew/Cellar/inih/58/lib]
Names: [INIReader]
Test case: Multiple library paths
Input: -L/opt/homebrew/lib -L/usr/lib -llua
Paths: [/opt/homebrew/lib /usr/lib]
Names: [lua]
Test case: No valid library
Input: -L/opt/homebrew/lib
Paths: [/opt/homebrew/lib]
Names: []
=== Test GenDylibPaths ===
Test case: existing dylib
Path libsymb1 is in the expected paths
Test case: existing dylibs
Path libsymb1 is in the expected paths
Path libsymb2 is in the expected paths
Test case: existint default paths
Path libsymb1 is in the expected paths
Path libsymb3 is in the expected paths
Test case: existint default paths & not found
notFounds [math]
Path libsymb1 is in the expected paths
Path libsymb3 is in the expected paths
Test case: no existing dylib
notFounds [notexist]
Error: failed to find any libraries
=== Test ParseCFlags ===
Test case: Single include path
Input: -I/usr/include
Paths: [/usr/include]
Test case: Multiple include paths
Input: -I/usr/include -I/opt/homebrew/include
Paths: [/usr/include /opt/homebrew/include]
Test case: Include paths mixed with other flags
Input: -I/usr/include -DDEBUG -I/opt/local/include -Wall
Paths: [/usr/include /opt/local/include]
Test case: Empty input
Input:
Paths: []
=== Test GenHeaderFilePath ===
Test case: Valid files
Input files: [test1.h test2.h]
Output: [test1.h test2.h]
Test case: Mixed existing and non-existing files
Input files: [test1.h nonexistent.h]
notFounds [nonexistent.h]
Output: [test1.h]
Test case: Multiple include paths
Input files: [test1.h test2.h test3.h]
Output: [test1.h test2.h test3.h]
Test case: No existing files
Input files: [nonexistent1.h nonexistent2.h]
Error: failed to find any header files
notFounds [nonexistent1.h nonexistent2.h]
Test case: Empty file list
Input files: []
Error: failed to find any header files
#stderr
#exit 0

View File

@@ -1,44 +0,0 @@
#stdout
=== Test NewSymbolProcessor ===
Before: No prefixes After: Prefixes: [lua_ luaL_]
=== Test RemovePrefix ===
Before: lua_closethread After: closethread
Before: luaL_checknumber After: checknumber
=== Test ToGoName ===
Before: lua_closethread After: Closethread
Before: luaL_checknumber After: Checknumber
Before: sqlite3_close_v2 After: CloseV2
Before: sqlite3_callback After: Callback
Before: GetReal After: GetReal
Before: GetBoolean After: GetBoolean
Before: INIReader After: Reader
=== Test GenMethodName ===
Before: Class: INIReader, Name: INIReader After: (*INIReader).Init
Before: Class: INIReader, Name: INIReader After: (*INIReader).Dispose
Before: Class: INIReader, Name: HasValue After: (*INIReader).HasValue
=== Test AddSuffix ===
Before: Class: INIReader, Method: INIReader After: (*Reader).Init
Before: Class: INIReader, Method: INIReader After: (*Reader).Init__1
Before: Class: INIReader, Method: ParseError After: (*Reader).ParseError
Before: Class: INIReader, Method: HasValue After: (*Reader).HasValue
=== Test Case: C++ Class with Methods ===
Parsed Symbols:
Symbol Map GoName: (*Reader).Init__1, ProtoName In HeaderFile: INIReader::INIReader(const char *, int), MangledName: _ZN9INIReaderC1EPKci
Symbol Map GoName: (*Reader).Init, ProtoName In HeaderFile: INIReader::INIReader(const int &), MangledName: _ZN9INIReaderC1ERKi
Symbol Map GoName: (*Reader).Dispose, ProtoName In HeaderFile: INIReader::~INIReader(), MangledName: _ZN9INIReaderD1Ev
Symbol Map GoName: (*Reader).ParseError, ProtoName In HeaderFile: INIReader::ParseError(), MangledName: _ZNK9INIReader10ParseErrorEv
=== Test Case: C Functions ===
Parsed Symbols:
Symbol Map GoName: Compare, ProtoName In HeaderFile: lua_compare(lua_State *, int, int, int), MangledName: lua_compare
Symbol Map GoName: Rawequal, ProtoName In HeaderFile: lua_rawequal(lua_State *, int, int), MangledName: lua_rawequal
#stderr
#exit 0

View File

@@ -1,165 +0,0 @@
package main
import (
"fmt"
"sort"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
)
func main() {
TestNewSymbolProcessor()
TestRemovePrefix()
TestToGoName()
TestGenMethodName()
TestAddSuffix()
TestParseHeaderFile()
}
func TestNewSymbolProcessor() {
fmt.Println("=== Test NewSymbolProcessor ===")
process := parse.NewSymbolProcessor([]string{"lua_", "luaL_"})
fmt.Printf("Before: No prefixes After: Prefixes: %v\n", process.Prefixes)
fmt.Println()
}
func TestRemovePrefix() {
fmt.Println("=== Test RemovePrefix ===")
process := parse.NewSymbolProcessor([]string{"lua_", "luaL_"})
testCases := []string{"lua_closethread", "luaL_checknumber"}
for _, input := range testCases {
result := process.TrimPrefixes(input)
fmt.Printf("Before: %s After: %s\n", input, result)
}
fmt.Println()
}
func TestToGoName() {
fmt.Println("=== Test ToGoName ===")
process1 := parse.NewSymbolProcessor([]string{"lua_", "luaL_"})
process2 := parse.NewSymbolProcessor([]string{"sqlite3_", "sqlite3_"})
process3 := parse.NewSymbolProcessor([]string{"INI"})
testCases := []struct {
processor *parse.SymbolProcessor
input string
}{
{process1, "lua_closethread"},
{process1, "luaL_checknumber"},
{process2, "sqlite3_close_v2"},
{process2, "sqlite3_callback"},
{process3, "GetReal"},
{process3, "GetBoolean"},
{process3, "INIReader"},
}
for _, tc := range testCases {
result := tc.processor.ToGoName(tc.input)
fmt.Printf("Before: %s After: %s\n", tc.input, result)
}
fmt.Println()
}
func TestGenMethodName() {
fmt.Println("=== Test GenMethodName ===")
process := &parse.SymbolProcessor{}
testCases := []struct {
class string
name string
isDestructor bool
}{
{"INIReader", "INIReader", false},
{"INIReader", "INIReader", true},
{"INIReader", "HasValue", false},
}
for _, tc := range testCases {
input := fmt.Sprintf("Class: %s, Name: %s", tc.class, tc.name)
result := process.GenMethodName(tc.class, tc.name, tc.isDestructor)
fmt.Printf("Before: %s After: %s\n", input, result)
}
fmt.Println()
}
func TestAddSuffix() {
fmt.Println("=== Test AddSuffix ===")
process := parse.NewSymbolProcessor([]string{"INI"})
methods := []string{
"INIReader",
"INIReader",
"ParseError",
"HasValue",
}
for _, method := range methods {
goName := process.ToGoName(method)
className := process.ToGoName("INIReader")
methodName := process.GenMethodName(className, goName, false)
finalName := process.AddSuffix(methodName)
input := fmt.Sprintf("Class: INIReader, Method: %s", method)
fmt.Printf("Before: %s After: %s\n", input, finalName)
}
fmt.Println()
}
func TestParseHeaderFile() {
testCases := []struct {
name string
content string
isCpp bool
prefixes []string
}{
{
name: "C++ Class with Methods",
content: `
class INIReader {
public:
INIReader(const std::string &filename);
INIReader(const char *buffer, size_t buffer_size);
~INIReader();
int ParseError() const;
private:
static std::string MakeKey(const std::string &section, const std::string &name);
};
`,
isCpp: true,
prefixes: []string{"INI"},
},
{
name: "C Functions",
content: `
typedef struct lua_State lua_State;
int(lua_rawequal)(lua_State *L, int idx1, int idx2);
int(lua_compare)(lua_State *L, int idx1, int idx2, int op);
`,
isCpp: false,
prefixes: []string{"lua_"},
},
}
for _, tc := range testCases {
fmt.Printf("=== Test Case: %s ===\n", tc.name)
symbolMap, err := parse.ParseHeaderFile([]string{tc.content}, tc.prefixes, tc.isCpp, true)
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
fmt.Println("Parsed Symbols:")
var keys []string
for key := range symbolMap {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
info := symbolMap[key]
fmt.Printf("Symbol Map GoName: %s, ProtoName In HeaderFile: %s, MangledName: %s\n", info.GoName, info.ProtoName, key)
}
fmt.Println()
}
}

View File

@@ -1,46 +0,0 @@
#stdout
=== Test GetCommonSymbols ===
Test Case: Lua symbols
Common Symbols (4):
Mangle: lua_absindex, CPP: lua_absindex(lua_State *, int), Go: Absindex
Mangle: lua_arith, CPP: lua_arith(lua_State *, int), Go: Arith
Mangle: lua_atpanic, CPP: lua_atpanic(lua_State *, lua_CFunction), Go: Atpanic
Mangle: lua_callk, CPP: lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction), Go: Callk
Test Case: INIReader and Std library symbols
Common Symbols (3):
Mangle: _ZNK9INIReader12GetInteger64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_x, CPP: INIReader::GetInteger64(const std::string &, const std::string &, int64_t), Go: (*Reader).GetInteger64
Mangle: _ZNK9INIReader7GetRealERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_d, CPP: INIReader::GetReal(const std::string &, const std::string &, double), Go: (*Reader).GetReal
Mangle: _ZNK9INIReader10ParseErrorEv, CPP: INIReader::ParseError(), Go: (*Reader).ParseError
=== Test ReadExistingSymbolTable ===
Symbols read from the file:
Symbol Map GoName: (*Reader).Init__1, ProtoName In HeaderFile: INIReader::INIReader(const char *, size_t), MangledName: _ZN9INIReaderC1EPKcm
Symbol Map GoName: (*Reader).GetBoolean, ProtoName In HeaderFile: INIReader::GetBoolean(const std::string &, const std::string &, bool), MangledName: _ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b
Symbol Map GoName: (*Reader).ParseError, ProtoName In HeaderFile: INIReader::ParseError(), MangledName: _ZNK9INIReader10ParseErrorEv
Havent existed symb file
=== Test GenSymbolTableData ===
[{
"mangle": "lua_absindex",
"c++": "lua_absindex(lua_State *, int)",
"go": "Absindex"
}, {
"mangle": "lua_arith",
"c++": "lua_arith(lua_State *, int)",
"go": "Arith"
}, {
"mangle": "lua_atpanic",
"c++": "lua_atpanic(lua_State *, lua_CFunction)",
"go": "Atpanic"
}, {
"mangle": "lua_callk",
"c++": "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)",
"go": "ModifiedCallk"
}]
#stderr
#exit 0

View File

@@ -1,152 +0,0 @@
package main
import (
"fmt"
"os"
"sort"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol"
"github.com/goplus/llgo/chore/llcppg/types"
"github.com/goplus/llgo/xtool/nm"
)
func main() {
TestGetCommonSymbols()
TestReadExistingSymbolTable()
TestGenSymbolTableData()
}
func TestGetCommonSymbols() {
fmt.Println("=== Test GetCommonSymbols ===")
testCases := []struct {
name string
dylibSymbols []*nm.Symbol
headerSymbols map[string]*parse.SymbolInfo
}{
{
name: "Lua symbols",
dylibSymbols: []*nm.Symbol{
{Name: symbol.AddSymbolPrefixUnder("lua_absindex", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_arith", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_atpanic", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_callk", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_lib_nonexistent", false)},
},
headerSymbols: map[string]*parse.SymbolInfo{
"lua_absindex": {ProtoName: "lua_absindex(lua_State *, int)", GoName: "Absindex"},
"lua_arith": {ProtoName: "lua_arith(lua_State *, int)", GoName: "Arith"},
"lua_atpanic": {ProtoName: "lua_atpanic(lua_State *, lua_CFunction)", GoName: "Atpanic"},
"lua_callk": {ProtoName: "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", GoName: "Callk"},
"lua_header_nonexistent": {ProtoName: "lua_header_nonexistent()", GoName: "HeaderNonexistent"},
},
},
{
name: "INIReader and Std library symbols",
dylibSymbols: []*nm.Symbol{
{Name: symbol.AddSymbolPrefixUnder("ZNK9INIReader12GetInteger64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_x", true)},
{Name: symbol.AddSymbolPrefixUnder("ZNK9INIReader7GetRealERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_d", true)},
{Name: symbol.AddSymbolPrefixUnder("ZNK9INIReader10ParseErrorEv", true)},
},
headerSymbols: map[string]*parse.SymbolInfo{
"_ZNK9INIReader12GetInteger64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_x": {GoName: "(*Reader).GetInteger64", ProtoName: "INIReader::GetInteger64(const std::string &, const std::string &, int64_t)"},
"_ZNK9INIReader13GetUnsigned64ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_y": {GoName: "(*Reader).GetUnsigned64", ProtoName: "INIReader::GetUnsigned64(const std::string &, const std::string &, uint64_t)"},
"_ZNK9INIReader7GetRealERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_d": {GoName: "(*Reader).GetReal", ProtoName: "INIReader::GetReal(const std::string &, const std::string &, double)"},
"_ZNK9INIReader10ParseErrorEv": {GoName: "(*Reader).ParseError", ProtoName: "INIReader::ParseError()"},
"_ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b": {GoName: "(*Reader).GetBoolean", ProtoName: "INIReader::GetBoolean(const std::string &, const std::string &, bool)"},
},
},
}
for _, tc := range testCases {
fmt.Printf("\nTest Case: %s\n", tc.name)
commonSymbols := symbol.GetCommonSymbols(tc.dylibSymbols, tc.headerSymbols)
fmt.Printf("Common Symbols (%d):\n", len(commonSymbols))
for _, sym := range commonSymbols {
fmt.Printf("Mangle: %s, CPP: %s, Go: %s\n", sym.Mangle, sym.CPP, sym.Go)
}
}
fmt.Println()
}
func TestReadExistingSymbolTable() {
fmt.Println("=== Test ReadExistingSymbolTable ===")
tmpFile, err := os.CreateTemp("", "llcppg.symb.json")
if err != nil {
fmt.Printf("Failed to create temp file: %v\n", err)
return
}
defer os.Remove(tmpFile.Name())
testData := `[
{
"mangle": "_ZN9INIReaderC1EPKcm",
"c++": "INIReader::INIReader(const char *, size_t)",
"go": "(*Reader).Init__1"
},
{
"mangle": "_ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b",
"c++": "INIReader::GetBoolean(const std::string &, const std::string &, bool)",
"go": "(*Reader).GetBoolean"
},
{
"mangle": "_ZNK9INIReader10ParseErrorEv",
"c++": "INIReader::ParseError()",
"go": "(*Reader).ParseError"
}
]`
if _, err := tmpFile.Write([]byte(testData)); err != nil {
fmt.Printf("Failed to write test data: %v\n", err)
return
}
tmpFile.Close()
symbols, exist := symbol.ReadExistingSymbolTable(tmpFile.Name())
if !exist {
fmt.Printf("ReadExistingSymbolTable failed")
return
}
fmt.Println("Symbols read from the file:")
var keys []string
for key := range symbols {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
info := symbols[key]
fmt.Printf("Symbol Map GoName: %s, ProtoName In HeaderFile: %s, MangledName: %s\n",
info.Go, info.CPP, key)
}
_, exist = symbol.ReadExistingSymbolTable("other.json")
if !exist {
fmt.Println("Havent existed symb file")
}
fmt.Println()
}
func TestGenSymbolTableData() {
fmt.Println("=== Test GenSymbolTableData ===")
commonSymbols := []*types.SymbolInfo{
{Mangle: "lua_absindex", CPP: "lua_absindex(lua_State *, int)", Go: "Absindex"},
{Mangle: "lua_arith", CPP: "lua_arith(lua_State *, int)", Go: "Arith"},
{Mangle: "lua_atpanic", CPP: "lua_atpanic(lua_State *, lua_CFunction)", Go: "Atpanic"},
{Mangle: "lua_callk", CPP: "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", Go: "Callk"},
}
existingSymbols := map[string]types.SymbolInfo{
"lua_absindex": {Mangle: "lua_absindex", CPP: "lua_absindex(lua_State *, int)", Go: "Absindex"},
"lua_arith": {Mangle: "lua_arith", CPP: "lua_arith(lua_State *, int)", Go: "Arith"},
"lua_callk": {Mangle: "lua_callk", CPP: "lua_callk(lua_State *, int, int, lua_KContext, lua_KFunction)", Go: "ModifiedCallk"},
}
data, err := symbol.GenSymbolTableData(commonSymbols, existingSymbols)
if err != nil {
fmt.Printf("Error generating symbol table data: %v\n", err)
return
}
fmt.Println(string(data))
fmt.Println()
}

View File

@@ -1,45 +0,0 @@
#stdout
=== Test Case: inireader ===
[{
"mangle": "_ZN9INIReaderC1EPKc",
"c++": "INIReader::INIReader(const char *)",
"go": "(*Reader).Init"
}, {
"mangle": "_ZN9INIReaderC1EPKcl",
"c++": "INIReader::INIReader(const char *, long)",
"go": "(*Reader).Init__1"
}, {
"mangle": "_ZN9INIReaderD1Ev",
"c++": "INIReader::~INIReader()",
"go": "(*Reader).Dispose"
}, {
"mangle": "_ZNK9INIReader10ParseErrorEv",
"c++": "INIReader::ParseError()",
"go": "(*Reader).ModifyedParseError"
}, {
"mangle": "_ZNK9INIReader3GetEPKcS1_S1_",
"c++": "INIReader::Get(const char *, const char *, const char *)",
"go": "(*Reader).Get"
}]
=== Test Case: lua ===
[{
"mangle": "lua_error",
"c++": "lua_error(lua_State *)",
"go": "Error"
}, {
"mangle": "lua_next",
"c++": "lua_next(lua_State *, int)",
"go": "Next"
}, {
"mangle": "lua_concat",
"c++": "lua_concat(lua_State *, int)",
"go": "Concat"
}, {
"mangle": "lua_stringtonumber",
"c++": "lua_stringtonumber(lua_State *, const char *)",
"go": "Stringtonumber"
}]
#stderr
#exit 0

View File

@@ -1,117 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol"
"github.com/goplus/llgo/xtool/nm"
)
func main() {
TestParseHeaderFile()
}
func TestParseHeaderFile() {
testCases := []struct {
name string
content string
isCpp bool
prefixes []string
dylibSymbols []*nm.Symbol
symbFileContent string
}{
{
name: "inireader",
content: `
#define INI_API __attribute__((visibility("default")))
class INIReader {
public:
__attribute__((visibility("default"))) explicit INIReader(const char *filename);
INI_API explicit INIReader(const char *buffer, long buffer_size);
~INIReader();
INI_API int ParseError() const;
INI_API const char * Get(const char *section, const char *name,
const char *default_value) const;
private:
static const char * MakeKey(const char *section, const char *name);
};
`,
isCpp: true,
prefixes: []string{"INI"},
dylibSymbols: []*nm.Symbol{
{Name: symbol.AddSymbolPrefixUnder("ZN9INIReaderC1EPKc", true)},
{Name: symbol.AddSymbolPrefixUnder("ZN9INIReaderC1EPKcl", true)},
{Name: symbol.AddSymbolPrefixUnder("ZN9INIReaderD1Ev", true)},
{Name: symbol.AddSymbolPrefixUnder("ZNK9INIReader10ParseErrorEv", true)},
{Name: symbol.AddSymbolPrefixUnder("ZNK9INIReader3GetEPKcS1_S1_", true)},
},
symbFileContent: `
[{
"mangle": "_ZN9INIReaderC1EPKc",
"c++": "INIReader::INIReader(const char *)",
"go": "(*Reader).Init"
}, {
"mangle": "_ZN9INIReaderC1EPKcl",
"c++": "INIReader::INIReader(const char *, long)",
"go": "(*Reader).Init__1"
}, {
"mangle": "_ZN9INIReaderD1Ev",
"c++": "INIReader::~INIReader()",
"go": "(*Reader).Dispose"
}, {
"mangle": "_ZNK9INIReader10ParseErrorEv",
"c++": "INIReader::ParseError()",
"go": "(*Reader).ModifyedParseError"
}]`,
},
{
name: "lua",
content: `
typedef struct lua_State lua_State;
LUA_API int(lua_error)(lua_State *L);
LUA_API int(lua_next)(lua_State *L, int idx);
LUA_API void(lua_concat)(lua_State *L, int n);
LUA_API void(lua_len)(lua_State *L, int idx);
LUA_API long unsigned int(lua_stringtonumber)(lua_State *L, const char *s);
LUA_API void(lua_setallocf)(lua_State *L, lua_Alloc f, void *ud);
LUA_API void(lua_toclose)(lua_State *L, int idx);
LUA_API void(lua_closeslot)(lua_State *L, int idx);
`,
isCpp: false,
prefixes: []string{"lua_"},
dylibSymbols: []*nm.Symbol{
{Name: symbol.AddSymbolPrefixUnder("lua_error", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_next", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_concat", false)},
{Name: symbol.AddSymbolPrefixUnder("lua_stringtonumber", false)},
},
},
}
for _, tc := range testCases {
fmt.Printf("=== Test Case: %s ===\n", tc.name)
headerSymbolMap, err := parse.ParseHeaderFile([]string{tc.content}, tc.prefixes, tc.isCpp, true)
if err != nil {
fmt.Println("Error:", err)
}
tmpFile, err := os.CreateTemp("", "llcppg.symb.json")
if err != nil {
fmt.Printf("Failed to create temp file: %v\n", err)
return
}
tmpFile.Write([]byte(tc.symbFileContent))
symbolData, err := symbol.GenerateAndUpdateSymbolTable(tc.dylibSymbols, headerSymbolMap, tmpFile.Name())
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(string(symbolData))
os.Remove(tmpFile.Name())
}
}

View File

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

View File

@@ -1,101 +0,0 @@
package clangutils
import (
"errors"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/clang"
)
type Config struct {
File string
Temp bool
Args []string
IsCpp bool
Index *clang.Index
}
type Visitor func(cursor, parent clang.Cursor) clang.ChildVisitResult
const TEMP_FILE = "temp.h"
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)
}
var index *clang.Index
if config.Index != nil {
index = config.Index
} else {
index = clang.CreateIndex(0, 0)
}
var unit *clang.TranslationUnit
if config.Temp {
content := c.AllocaCStr(config.File)
tempFile := &clang.UnsavedFile{
Filename: c.Str(TEMP_FILE),
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 GetLocation(loc clang.SourceLocation) (file clang.File, line c.Uint, column c.Uint, offset c.Uint) {
loc.SpellingLocation(&file, &line, &column, &offset)
return
}
// Traverse up the semantic parents
func BuildScopingParts(cursor clang.Cursor) []string {
var parts []string
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 VisitChildren(cursor clang.Cursor, fn Visitor) c.Uint {
return clang.VisitChildren(cursor, func(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
cfn := *(*Visitor)(clientData)
return cfn(cursor, parent)
}, unsafe.Pointer(&fn))
}

View File

@@ -1,109 +0,0 @@
package cfgparse
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
// Note: This package is not placed under the 'config' package because 'config'
// depends on 'cjson'. The parsing of Libs and cflags is intended to be usable
// by both llgo and go, without introducing additional dependencies.
type Libs struct {
Paths []string // Dylib Path
Names []string
}
type CFlags struct {
Paths []string // Include Path
}
func ParseLibs(libs string) *Libs {
parts := strings.Fields(libs)
lbs := &Libs{}
for _, part := range parts {
if strings.HasPrefix(part, "-L") {
lbs.Paths = append(lbs.Paths, part[2:])
} else if strings.HasPrefix(part, "-l") {
lbs.Names = append(lbs.Names, part[2:])
}
}
return lbs
}
// searches for each library name in the provided paths and default paths,
// appending the appropriate file extension (.dylib for macOS, .so for Linux).
//
// Example: For "-L/opt/homebrew/lib -llua -lm":
// - It will search for liblua.dylib (on macOS) or liblua.so (on Linux)
// - System libs like -lm are ignored and included in notFound
//
// So error is returned if no libraries found at all.
func (l *Libs) GenDylibPaths(defaultPaths []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
affix := ".dylib"
if runtime.GOOS == "linux" {
affix = ".so"
}
searchPaths := append(l.Paths, defaultPaths...)
for _, name := range l.Names {
var foundPath string
for _, path := range searchPaths {
dylibPath := filepath.Join(path, "lib"+name+affix)
if _, err := os.Stat(dylibPath); err == nil {
foundPath = dylibPath
break
}
}
if foundPath != "" {
foundPaths = append(foundPaths, foundPath)
} else {
notFound = append(notFound, name)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any libraries")
}
return foundPaths, notFound, nil
}
func ParseCFlags(cflags string) *CFlags {
parts := strings.Fields(cflags)
cf := &CFlags{}
for _, part := range parts {
if strings.HasPrefix(part, "-I") {
cf.Paths = append(cf.Paths, part[2:])
}
}
return cf
}
func (cf *CFlags) GenHeaderFilePaths(files []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
for _, file := range files {
var found bool
for _, path := range cf.Paths {
fullPath := filepath.Join(path, file)
if _, err := os.Stat(fullPath); err == nil {
foundPaths = append(foundPaths, fullPath)
found = true
break
}
}
if !found {
notFound = append(notFound, file)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any header files")
}
return foundPaths, notFound, nil
}

View File

@@ -1,72 +0,0 @@
package config
import (
"errors"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/llcppg/types"
)
type Conf struct {
*cjson.JSON
*types.Config
}
func GetConf(data []byte) (Conf, error) {
parsedConf := cjson.ParseBytes(data)
if parsedConf == nil {
return Conf{}, errors.New("failed to parse config")
}
config := &types.Config{
Name: GetStringItem(parsedConf, "name", ""),
CFlags: GetStringItem(parsedConf, "cflags", ""),
Libs: GetStringItem(parsedConf, "libs", ""),
Include: GetStringArrayItem(parsedConf, "include"),
TrimPrefixes: GetStringArrayItem(parsedConf, "trimPrefixes"),
Cplusplus: GetBoolItem(parsedConf, "cplusplus"),
}
return Conf{
JSON: parsedConf,
Config: config,
}, nil
}
func GetString(obj *cjson.JSON) (value string) {
str := obj.GetStringValue()
return unsafe.String((*byte)(unsafe.Pointer(str)), c.Strlen(str))
}
func GetStringItem(obj *cjson.JSON, key string, defval string) (value string) {
item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key))
if item == nil {
return defval
}
return GetString(item)
}
func GetStringArrayItem(obj *cjson.JSON, key string) (value []string) {
item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key))
if item == nil {
return
}
value = make([]string, item.GetArraySize())
for i := range value {
value[i] = GetString(item.GetArrayItem(c.Int(i)))
}
return
}
func GetBoolItem(obj *cjson.JSON, key string) bool {
item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key))
if item == nil {
return false
}
if item.IsBool() != 0 {
return item.IsTrue() != 0
}
return false
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"io"
"os"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/args"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config/cfgparse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol"
)
func main() {
symbFile := "llcppg.symb.json"
ags, _ := args.ParseArgs(os.Args[1:], nil)
var data []byte
var err error
if ags.UseStdin {
data, err = io.ReadAll(os.Stdin)
} else {
data, err = os.ReadFile(ags.CfgFile)
}
check(err)
conf, err := config.GetConf(data)
check(err)
defer conf.Delete()
if ags.Verbose {
symbol.SetDebug(symbol.DbgFlagAll)
if ags.UseStdin {
fmt.Println("Config From Stdin")
} else {
fmt.Println("Config From File", ags.CfgFile)
}
fmt.Println("Name:", conf.Name)
fmt.Println("CFlags:", conf.CFlags)
fmt.Println("Libs:", conf.Libs)
fmt.Println("Include:", conf.Include)
fmt.Println("TrimPrefixes:", conf.TrimPrefixes)
fmt.Println("Cplusplus:", conf.Cplusplus)
}
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to parse config file:", ags.CfgFile)
}
symbols, err := symbol.ParseDylibSymbols(conf.Libs)
check(err)
cflag := cfgparse.ParseCFlags(conf.CFlags)
filepaths, notFounds, err := cflag.GenHeaderFilePaths(conf.Include)
check(err)
if ags.Verbose {
fmt.Println("header file paths", filepaths)
if len(notFounds) > 0 {
fmt.Println("not found header files", notFounds)
}
}
headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes, conf.Cplusplus, false)
check(err)
symbolData, err := symbol.GenerateAndUpdateSymbolTable(symbols, headerInfos, symbFile)
check(err)
err = os.WriteFile(symbFile, symbolData, 0644)
check(err)
}
func check(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -1,190 +0,0 @@
package parse
import (
"errors"
"runtime"
"strconv"
"strings"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
)
type SymbolInfo struct {
GoName string
ProtoName string
}
type SymbolProcessor struct {
Prefixes []string
SymbolMap map[string]*SymbolInfo
CurrentFile string
NameCounts map[string]int
}
func NewSymbolProcessor(Prefixes []string) *SymbolProcessor {
return &SymbolProcessor{
Prefixes: Prefixes,
SymbolMap: make(map[string]*SymbolInfo),
NameCounts: make(map[string]int),
}
}
func (p *SymbolProcessor) setCurrentFile(filename string) {
p.CurrentFile = filename
}
func (p *SymbolProcessor) TrimPrefixes(str string) string {
for _, prefix := range p.Prefixes {
if strings.HasPrefix(str, prefix) {
return strings.TrimPrefix(str, prefix)
}
}
return str
}
func toTitle(s string) string {
if s == "" {
return ""
}
return strings.ToUpper(s[:1]) + (s[1:])
}
func toUpperCamelCase(originName string) string {
if originName == "" {
return ""
}
subs := strings.Split(string(originName), "_")
name := ""
for _, sub := range subs {
name += toTitle(sub)
}
return name
}
// 1. remove prefix from config
// 2. convert to camel case
func (p *SymbolProcessor) ToGoName(name string) string {
return toUpperCamelCase(p.TrimPrefixes(name))
}
func (p *SymbolProcessor) GenMethodName(class, name string, isDestructor bool) string {
prefix := "(*" + class + ")."
if isDestructor {
return prefix + "Dispose"
}
if class == name {
return prefix + "Init"
}
return prefix + name
}
func (p *SymbolProcessor) genGoName(cursor clang.Cursor) string {
funcName := cursor.String()
defer funcName.Dispose()
originName := c.GoString(funcName.CStr())
isDestructor := cursor.Kind == clang.CursorDestructor
var convertedName string
if isDestructor {
convertedName = p.ToGoName(originName[1:])
} else {
convertedName = p.ToGoName(originName)
}
if parent := cursor.SemanticParent(); parent.Kind == clang.CursorClassDecl {
parentName := parent.String()
defer parentName.Dispose()
class := p.ToGoName(c.GoString(parentName.CStr()))
return p.AddSuffix(p.GenMethodName(class, convertedName, isDestructor))
}
return p.AddSuffix(convertedName)
}
func (p *SymbolProcessor) genProtoName(cursor clang.Cursor) string {
displayName := cursor.DisplayName()
defer displayName.Dispose()
scopingParts := clangutils.BuildScopingParts(cursor.SemanticParent())
var builder strings.Builder
for _, part := range scopingParts {
builder.WriteString(part)
builder.WriteString("::")
}
builder.WriteString(c.GoString(displayName.CStr()))
return builder.String()
}
func (p *SymbolProcessor) AddSuffix(name string) string {
p.NameCounts[name]++
if count := p.NameCounts[name]; count > 1 {
return name + "__" + strconv.Itoa(count-1)
}
return name
}
func (p *SymbolProcessor) collectFuncInfo(cursor clang.Cursor) {
symbol := cursor.Mangling()
defer symbol.Dispose()
// On Linux, C++ symbols typically have one leading underscore
// On macOS, C++ symbols may have two leading underscores
// For consistency, we remove the first leading underscore on macOS
symbolName := c.GoString(symbol.CStr())
if runtime.GOOS == "darwin" {
symbolName = strings.TrimPrefix(symbolName, "_")
}
p.SymbolMap[symbolName] = &SymbolInfo{
GoName: p.genGoName(cursor),
ProtoName: p.genProtoName(cursor),
}
}
func (p *SymbolProcessor) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResult {
switch cursor.Kind {
case clang.CursorNamespace, clang.CursorClassDecl:
clangutils.VisitChildren(cursor, p.visitTop)
case clang.CursorCXXMethod, clang.CursorFunctionDecl, clang.CursorConstructor, clang.CursorDestructor:
loc := cursor.Location()
file, _, _, _ := clangutils.GetLocation(loc)
filename := file.FileName()
defer filename.Dispose()
isCurrentFile := c.Strcmp(filename.CStr(), c.AllocaCStr(p.CurrentFile)) == 0
isPublicMethod := (cursor.CXXAccessSpecifier() == clang.CXXPublic) && cursor.Kind == clang.CursorCXXMethod || cursor.Kind == clang.CursorConstructor || cursor.Kind == clang.CursorDestructor
if isCurrentFile && (cursor.Kind == clang.CursorFunctionDecl || isPublicMethod) {
p.collectFuncInfo(cursor)
}
}
return clang.ChildVisit_Continue
}
func ParseHeaderFile(files []string, Prefixes []string, isCpp bool, isTemp bool) (map[string]*SymbolInfo, error) {
processer := NewSymbolProcessor(Prefixes)
index := clang.CreateIndex(0, 0)
for _, file := range files {
_, unit, err := clangutils.CreateTranslationUnit(&clangutils.Config{
File: file,
Temp: isTemp,
IsCpp: isCpp,
Index: index,
})
if err != nil {
return nil, errors.New("Unable to parse translation unit for file " + file)
}
cursor := unit.Cursor()
if isTemp {
processer.setCurrentFile(clangutils.TEMP_FILE)
} else {
processer.setCurrentFile(file)
}
clangutils.VisitChildren(cursor, processer.visitTop)
unit.Dispose()
}
index.Dispose()
return processer.SymbolMap, nil
}

View File

@@ -1,283 +0,0 @@
package symbol
import (
"errors"
"fmt"
"os"
"runtime"
"strings"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config/cfgparse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/llcppg/types"
"github.com/goplus/llgo/xtool/nm"
)
type dbgFlags = int
const (
DbgSymbol dbgFlags = 1 << iota
DbgFlagAll = DbgSymbol
)
var (
debugSymbol bool
)
func SetDebug(dbgFlags dbgFlags) {
debugSymbol = (dbgFlags & DbgSymbol) != 0
}
// ParseDylibSymbols parses symbols from dynamic libraries specified in the lib string.
// It handles multiple libraries (e.g., -L/opt/homebrew/lib -llua -lm) and returns
// symbols if at least one library is successfully parsed. Errors from inaccessible
// libraries (like standard libs) are logged as warnings.
//
// Returns symbols and nil error if any symbols are found, or nil and error if none found.
func ParseDylibSymbols(lib string) ([]*nm.Symbol, error) {
if debugSymbol {
fmt.Println("ParseDylibSymbols:from", lib)
}
sysPaths := getSysLibPaths()
lbs := cfgparse.ParseLibs(lib)
if debugSymbol {
fmt.Println("ParseDylibSymbols:LibConfig Parse To")
fmt.Println("libs.Names: ", lbs.Names)
fmt.Println("libs.Paths: ", lbs.Paths)
}
dylibPaths, notFounds, err := lbs.GenDylibPaths(sysPaths)
if err != nil {
return nil, fmt.Errorf("failed to generate some dylib paths: %v", err)
}
if debugSymbol {
fmt.Println("ParseDylibSymbols:dylibPaths", dylibPaths)
if len(notFounds) > 0 {
fmt.Println("ParseDylibSymbols:not found libname", notFounds)
} else {
fmt.Println("ParseDylibSymbols:every library is found")
}
}
var symbols []*nm.Symbol
var parseErrors []string
for _, dylibPath := range dylibPaths {
if _, err := os.Stat(dylibPath); err != nil {
if debugSymbol {
fmt.Printf("ParseDylibSymbols:Failed to access dylib %s: %v\n", dylibPath, err)
}
continue
}
files, err := nm.New("").List(dylibPath)
if err != nil {
parseErrors = append(parseErrors, fmt.Sprintf("ParseDylibSymbols:Failed to list symbols in dylib %s: %v", dylibPath, err))
continue
}
for _, file := range files {
symbols = append(symbols, file.Symbols...)
}
}
if len(symbols) > 0 {
if debugSymbol {
if len(parseErrors) > 0 {
fmt.Printf("ParseDylibSymbols:Some libraries could not be parsed: %v\n", parseErrors)
}
fmt.Println("ParseDylibSymbols:", len(symbols), "symbols")
}
return symbols, nil
}
return nil, fmt.Errorf("no symbols found in any dylib. Errors: %v", parseErrors)
}
func getSysLibPaths() []string {
var paths []string
if runtime.GOOS == "linux" {
if debugSymbol {
fmt.Println("getSysLibPaths:find sys lib path from linux")
}
paths = []string{
"/usr/lib",
"/usr/local/lib",
}
paths = append(paths, getPath("/etc/ld.so.conf")...)
if debugSymbol && len(paths) == 0 {
fmt.Println("getSysLibPaths:/etc/ld.so.conf havent find any path")
}
confd := "/etc/ld.so.conf.d"
dir, err := os.Stat(confd)
if err != nil || !dir.IsDir() {
if debugSymbol {
fmt.Println("getSysLibPaths:/etc/ld.so.conf.d not found or not dir")
}
return paths
}
// todo(zzy) : wait llgo os.ReadDir support
// files, err := os.ReadDir(confd)
// if err == nil {
// for _, file := range files {
// filepath := filepath.Join(confd, file.Name())
// paths = append(paths, getPath(filepath)...)
// }
// }
}
return paths
}
func getPath(file string) []string {
if debugSymbol {
fmt.Println("getPath:from", file)
}
var paths []string
content, err := os.ReadFile(file)
if err != nil {
return paths
}
lines := strings.Split(string(content), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "#") {
if file, err := os.Stat(line); err == nil && file.IsDir() {
paths = append(paths, line)
}
}
}
return paths
}
// finds the intersection of symbols from the dynamic library's symbol table and the symbols parsed from header files.
// It returns a list of symbols that can be externally linked.
func GetCommonSymbols(dylibSymbols []*nm.Symbol, headerSymbols map[string]*parse.SymbolInfo) []*types.SymbolInfo {
var commonSymbols []*types.SymbolInfo
for _, dylibSym := range dylibSymbols {
symName := dylibSym.Name
if runtime.GOOS == "darwin" {
symName = strings.TrimPrefix(symName, "_")
}
if symInfo, ok := headerSymbols[symName]; ok {
symbolInfo := &types.SymbolInfo{
Mangle: symName,
CPP: symInfo.ProtoName,
Go: symInfo.GoName,
}
commonSymbols = append(commonSymbols, symbolInfo)
}
}
return commonSymbols
}
func ReadExistingSymbolTable(fileName string) (map[string]types.SymbolInfo, bool) {
if _, err := os.Stat(fileName); err != nil {
return nil, false
}
data, err := os.ReadFile(fileName)
if err != nil {
return nil, false
}
parsedJSON := cjson.ParseBytes(data)
if parsedJSON == nil {
return nil, false
}
existingSymbols := make(map[string]types.SymbolInfo)
arraySize := parsedJSON.GetArraySize()
for i := 0; i < int(arraySize); i++ {
item := parsedJSON.GetArrayItem(c.Int(i))
symbol := types.SymbolInfo{
Mangle: config.GetStringItem(item, "mangle", ""),
CPP: config.GetStringItem(item, "c++", ""),
Go: config.GetStringItem(item, "go", ""),
}
existingSymbols[symbol.Mangle] = symbol
}
return existingSymbols, true
}
func GenSymbolTableData(commonSymbols []*types.SymbolInfo, existingSymbols map[string]types.SymbolInfo) ([]byte, error) {
if len(existingSymbols) > 0 {
if debugSymbol {
fmt.Println("GenSymbolTableData:generate symbol table with exist symbol table")
}
for i := range commonSymbols {
if existingSymbol, exists := existingSymbols[commonSymbols[i].Mangle]; exists && commonSymbols[i].Go != existingSymbol.Go {
if debugSymbol {
fmt.Println("symbol", commonSymbols[i].Mangle, "already exist, use exist symbol", existingSymbol.Go)
}
commonSymbols[i].Go = existingSymbol.Go
} else {
if debugSymbol {
fmt.Println("new symbol", commonSymbols[i].Mangle, "-", commonSymbols[i].CPP, "-", commonSymbols[i].Go)
}
}
}
} else {
if debugSymbol {
fmt.Println("GenSymbolTableData:generate symbol table without symbol table")
for _, symbol := range commonSymbols {
fmt.Println("new symbol", symbol.Mangle, "-", symbol.CPP, "-", symbol.Go)
}
}
}
root := cjson.Array()
defer root.Delete()
for _, symbol := range commonSymbols {
item := cjson.Object()
item.SetItem(c.Str("mangle"), cjson.String(c.AllocaCStr(symbol.Mangle)))
item.SetItem(c.Str("c++"), cjson.String(c.AllocaCStr(symbol.CPP)))
item.SetItem(c.Str("go"), cjson.String(c.AllocaCStr(symbol.Go)))
root.AddItem(item)
}
cStr := root.Print()
if cStr == nil {
return nil, errors.New("symbol table is empty")
}
defer c.Free(unsafe.Pointer(cStr))
result := []byte(c.GoString(cStr))
return result, nil
}
func GenerateAndUpdateSymbolTable(symbols []*nm.Symbol, headerInfos map[string]*parse.SymbolInfo, symbFile string) ([]byte, error) {
commonSymbols := GetCommonSymbols(symbols, headerInfos)
if debugSymbol {
fmt.Println("GenerateAndUpdateSymbolTable:", len(commonSymbols), "common symbols")
}
existSymbols, exist := ReadExistingSymbolTable(symbFile)
if exist && debugSymbol {
fmt.Println("GenerateAndUpdateSymbolTable:current path have exist symbol table", symbFile)
}
symbolData, err := GenSymbolTableData(commonSymbols, existSymbols)
if err != nil {
return nil, err
}
return symbolData, nil
}
// For mutiple os test,the nm output's symbol name is different.
func AddSymbolPrefixUnder(name string, isCpp bool) string {
prefix := ""
if runtime.GOOS == "darwin" {
prefix = prefix + "_"
}
if isCpp {
prefix = prefix + "_"
}
return prefix + name
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
func main() {
// TODO(xsw): implement gogensig tool
}

View File

@@ -1,27 +0,0 @@
llcppg - Autogen tool for C/C++ libraries
====
## Usage
```sh
llcppg [config-file]
```
If `config-file` is not specified, a `llcppg.cfg` file is used in current directory. The configuration file format is as follows:
```json
{
"name": "inireader",
"cflags": "$(pkg-config --cflags inireader)",
"include": [
"INIReader.h",
"AnotherHeaderFile.h"
],
"libs": "$(pkg-config --libs inireader)",
"trimPrefixes": ["Ini", "INI"]
}
```
## Design
See [llcppg Design](design.md).

View File

@@ -1,379 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ast
import "github.com/goplus/llgo/chore/llcppg/token"
// =============================================================================
type Node interface {
}
type Expr interface {
Node
exprNode()
}
type Decl interface {
Node
declNode()
}
type Stmt interface {
Node
stmtNode()
}
type PPD interface { // preprocessing directive
Node
ppdNode()
}
// =============================================================================
type AccessSpecifier uint
const (
Invalid AccessSpecifier = iota
Public
Protected
Private
)
// =============================================================================
// Expressions (Types are also expressions)
type BasicLitKind uint
const (
IntLit BasicLitKind = iota
FloatLit
CharLit
StringLit
)
type BasicLit struct {
Kind BasicLitKind
Value string
}
func (*BasicLit) exprNode() {}
// ------------------------------------------------
type TypeKind uint
const (
Void TypeKind = iota
Bool
Char
Char16
Char32
WChar
Int
Int128
Float
Float16
Float128
Complex
)
type TypeFlag uint
const (
Signed TypeFlag = 1 << iota
Unsigned
Long
LongLong
Double
Short
)
// [signed/unsigned/short/long/long long/double] [int]/char/float/complex/bool
type BuiltinType struct {
Kind TypeKind
Flags TypeFlag
}
func (*BuiltinType) exprNode() {}
// ------------------------------------------------
// Name
type Ident struct {
Name string
}
func (*Ident) exprNode() {}
// ------------------------------------------------
type Tag int
const (
Struct Tag = iota
Union
Enum
Class
)
// struct/union/enum/class (A::B::)Name
type TagExpr struct {
Tag Tag
Name Expr // ScopingExpr, Ident
}
func (*TagExpr) exprNode() {}
// ------------------------------------------------
// type a, ...
type Variadic struct {
}
func (*Variadic) exprNode() {}
// ------------------------------------------------
// (X)
type ParenExpr struct {
X Expr
}
func (*ParenExpr) exprNode() {}
// ------------------------------------------------
// Parent::X
type ScopingExpr struct {
Parent Expr
X Expr
}
func (*ScopingExpr) exprNode() {}
// ------------------------------------------------
// X*
type PointerType struct {
X Expr
}
func (*PointerType) exprNode() {}
// ------------------------------------------------
// X&
type LvalueRefType struct {
X Expr
}
func (*LvalueRefType) exprNode() {}
// X&&
type RvalueRefType struct {
X Expr
}
func (*RvalueRefType) exprNode() {}
// ------------------------------------------------
// Elt[Len]
// Elt[]
type ArrayType struct {
Elt Expr
Len Expr // optional
}
func (*ArrayType) exprNode() {}
// ------------------------------------------------
type Comment struct {
Text string // comment text (excluding '\n' for //-style comments)
}
func (*Comment) exprNode() {}
type CommentGroup struct {
List []*Comment // len(List) > 0
}
func (*CommentGroup) exprNode() {}
// ------------------------------------------------
type Field struct {
Doc *CommentGroup // associated documentation; or nil
Type Expr // field/method/parameter type; or nil
Names []*Ident // field/method/(type) parameter names; or nil
Comment *CommentGroup // line comments; or nil
Access AccessSpecifier // field access(Record Type); Struct Field default is Public,Class Field default is Private
IsStatic bool // static field
}
func (*Field) exprNode() {}
type FieldList struct {
List []*Field // field list; or nil
}
func (*FieldList) exprNode() {}
// ------------------------------------------------
// Ret (*)(Params)
type FuncType struct {
Params *FieldList
Ret Expr
}
func (*FuncType) exprNode() {}
// ------------------------------------------------
type RecordType struct {
Tag Tag
Fields *FieldList
Methods []*FuncDecl
}
func (*RecordType) exprNode() {}
// ------------------------------------------------
// Template<Arg1, Arg2, ...>
type InstantiationType struct {
Template Expr
Args *FieldList
}
func (*InstantiationType) exprNode() {}
// =============================================================================
// Declarations
type Location struct {
File string
}
type DeclBase struct {
Doc *CommentGroup // associated documentation; or nil
Loc *Location
Parent Expr // namespace or class
}
// ------------------------------------------------
// typedef Type Name;
type TypedefDecl struct {
DeclBase
Type Expr
Name *Ident
}
func (*TypedefDecl) declNode() {}
// ------------------------------------------------
type EnumItem struct {
Name *Ident
Value Expr // optional
}
func (*EnumItem) exprNode() {}
type EnumType struct {
Items []*EnumItem
}
func (*EnumType) exprNode() {}
// enum Name { Item1, Item2, ... };
type EnumTypeDecl struct {
DeclBase
Name *Ident
Type *EnumType
}
func (*EnumTypeDecl) declNode() {}
// ------------------------------------------------
// Ret Name(Params);
type FuncDecl struct {
DeclBase
Name *Ident
MangledName string // C: same as Name, C++: mangled
Type *FuncType
IsInline bool
IsStatic bool
// Class method specific fields
IsConst bool // const member function
IsExplicit bool // explicit constructor
IsConstructor bool
IsDestructor bool
IsVirtual bool
IsOverride bool
}
func (*FuncDecl) declNode() {}
// ------------------------------------------------
// struct/union/class Name { Field1, Field2, ... };
type TypeDecl struct {
DeclBase
Name *Ident
Type *RecordType
}
func (*TypeDecl) declNode() {}
// =============================================================================
// AST File
type Include struct {
Path string `json:"path"`
}
func (*Include) ppdNode() {}
// ------------------------------------------------
type Token struct {
Token token.Token
Lit string
}
type Macro struct {
Name string
Tokens []*Token // Tokens[0].Lit is the macro name
}
func (*Macro) ppdNode() {}
// ------------------------------------------------
type File struct {
Decls []Decl `json:"decls"`
Includes []*Include `json:"includes,omitempty"`
Macros []*Macro `json:"macros,omitempty"`
}
// =============================================================================

View File

@@ -1,85 +0,0 @@
llcppg Design
=====
## Usage
```sh
llcppg [config-file]
```
If `config-file` is not specified, a `llcppg.cfg` file is used in current directory. The configuration file format is as follows:
```json
{
"name": "inih",
"cflags": "$(pkg-config --cflags inireader)",
"include": [
"INIReader.h",
"AnotherHeaderFile.h"
],
"libs": "$(pkg-config --libs inireader)",
"trimPrefixes": ["Ini", "INI"],
"cplusplus":true
}
```
## Steps
1. llcppsymg: Generate symbol table for a C/C++ library
2. Manually modify the desired Go symbols in symbol table
3. llcppsigfetch: Fetch information of C/C++ symbols
4. gogensig: Generate a go package by information of symbols
### llcppsymg
```sh
llcppsymg config-file
llcppsymg - # read config from stdin
```
It generates a symbol table file named `llcppg.symb.json`. Its file format is as follows:
```json
[
{
"mangle": "_ZN9INIReaderC1EPKcm",
"c++": "INIReader::INIReader(char const*, unsigned long)",
"go": "(*Reader).Init__0"
}
]
```
### llcppsigfetch
```sh
llcppsigfetch config-file
llcppsigfetch - # read config from stdin
```
It fetches information of C/C++ symbols and print to stdout. Its format is as follows:
```json
[
{
"path": "/path/to/file.h",
"doc": {
"decls": [],
"macros": [],
"includes": [
{
"path": "incfile.h"
}
]
}
}
]
```
### gogensig
```sh
gogensig ast-file
gogensig - # read AST from stdin
```

View File

@@ -1,93 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"github.com/goplus/llgo/chore/llcppg/types"
"github.com/goplus/llgo/xtool/env"
)
func llcppsymg(conf []byte) error {
cmd := exec.Command("llcppsymg", "-")
cmd.Stdin = bytes.NewReader(conf)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func llcppsigfetch(conf []byte, out *io.PipeWriter) {
cmd := exec.Command("llcppsigfetch", "-")
cmd.Stdin = bytes.NewReader(conf)
cmd.Stdout = out
cmd.Stderr = os.Stderr
err := cmd.Run()
check(err)
out.Close()
}
func gogensig(in io.Reader) error {
cmd := exec.Command("gogensig", "-")
cmd.Stdin = in
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func main() {
cfgFile := "llcppg.cfg"
if len(os.Args) > 1 {
cfgFile = os.Args[1]
}
if cfgFile == "-h" || cfgFile == "--help" {
fmt.Fprintln(os.Stderr, "Usage: llcppg [config-file]")
return
}
f, err := os.Open(cfgFile)
check(err)
defer f.Close()
var conf types.Config
json.NewDecoder(f).Decode(&conf)
conf.CFlags = env.ExpandEnv(conf.CFlags)
conf.Libs = env.ExpandEnv(conf.Libs)
b, err := json.MarshalIndent(&conf, "", " ")
check(err)
err = llcppsymg(b)
check(err)
r, w := io.Pipe()
go llcppsigfetch(b, w)
err = gogensig(r)
check(err)
}
func check(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -1,32 +0,0 @@
package token
type Token uint
const (
ILLEGAL Token = iota
/**
* A token that contains some kind of punctuation.
*/
PUNCT
/**
* A language keyword.
*/
KEYWORD
/**
* An identifier (that is not a keyword).
*/
IDENT
/**
* A numeric, string, or character literal.
*/
LITERAL
/**
* A comment.
*/
COMMENT
)

View File

@@ -1,33 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package types
// Config represents a configuration for the llcppg tool.
type Config struct {
Name string `json:"name"`
CFlags string `json:"cflags"`
Libs string `json:"libs"`
Include []string `json:"include"`
TrimPrefixes []string `json:"trimPrefixes"`
Cplusplus bool `json:"cplusplus"`
}
type SymbolInfo struct {
Mangle string `json:"mangle"` // C++ Symbol
CPP string `json:"c++"` // C++ function name
Go string `json:"go"` // Go function name
}