diff --git a/cl/convert.go b/cl/convert.go index 20c7a525..dfe552a9 100644 --- a/cl/convert.go +++ b/cl/convert.go @@ -20,6 +20,22 @@ func ModInit(deps []string, outputDir string, modulePath string) error { type NodeConverter = convert.NodeConverter +type ProcessSymbol = convert.ProcessSymbol + +func NewProcessSymbol() *ProcessSymbol { + return convert.NewProcessSymbol() +} + +type NameMethod = convert.NameMethod + +type Node = convert.Node + +func NewNode(name string, kind NodeKind) Node { + return convert.NewNode(name, kind) +} + +type NodeKind = convert.NodeKind + type ConvConfig struct { OutputDir string PkgPath string @@ -28,6 +44,7 @@ type ConvConfig struct { FileMap map[string]*llconfig.FileInfo ConvSym func(name *ast.Object, mangleName string) (goName string, err error) NodeConv NodeConverter + Symbols *convert.ProcessSymbol // CfgFile string // llcppg.cfg TypeMap map[string]string // llcppg.pub @@ -46,6 +63,7 @@ func Convert(config *ConvConfig) (pkg Package, err error) { FileMap: config.FileMap, ConvSym: config.ConvSym, NodeConv: config.NodeConv, + Symbols: config.Symbols, TypeMap: config.TypeMap, Deps: config.Deps, diff --git a/cl/internal/convert/convert.go b/cl/internal/convert/convert.go index 2b6a9c47..0e271b4a 100644 --- a/cl/internal/convert/convert.go +++ b/cl/internal/convert/convert.go @@ -8,6 +8,7 @@ import ( "github.com/goplus/llcppg/ast" cfg "github.com/goplus/llcppg/cmd/gogensig/config" llconfig "github.com/goplus/llcppg/config" + ctoken "github.com/goplus/llcppg/token" ) var ( @@ -41,6 +42,7 @@ type Config struct { FileMap map[string]*llconfig.FileInfo ConvSym func(name *ast.Object, mangleName string) (goName string, err error) NodeConv NodeConverter + Symbols *ProcessSymbol // CfgFile string // llcppg.cfg TypeMap map[string]string // llcppg.pub @@ -94,6 +96,7 @@ func NewConverter(config *Config) (*Converter, error) { Name: config.PkgName, OutputDir: config.OutputDir, ConvSym: config.ConvSym, + Symbols: config.Symbols, LibCommand: config.Libs, TrimPrefixes: config.TrimPrefixes, KeepUnderScore: config.KeepUnderScore, @@ -123,10 +126,28 @@ func (p *Converter) Process() { } } + processNode := func(goFile string, process func() error) { + p.GenPkg.SetGoFile(goFile) + if err := process(); err != nil { + log.Panicln(err) + } + } + for _, macro := range p.Pkg.Macros { - processDecl(macro.Loc.File, func() error { - return p.GenPkg.NewMacro(macro) - }) + if len(macro.Tokens) == 2 && macro.Tokens[1].Token == ctoken.LITERAL { + goName, goFile, err := p.Conf.NodeConv.ConvMacro(macro) + // todo(zzy):goName to New Macro + if err != nil { + if errors.Is(err, ErrSkip) { + continue + } + // todo(zzy):refine error handing + log.Panicln(err) + } + processNode(goFile, func() error { + return p.GenPkg.NewMacro(macro, goName) + }) + } } for _, decl := range p.Pkg.Decls { diff --git a/cl/internal/convert/convert_test.go b/cl/internal/convert/convert_test.go index 9c7c8dd7..eb8b0056 100644 --- a/cl/internal/convert/convert_test.go +++ b/cl/internal/convert/convert_test.go @@ -13,6 +13,7 @@ import ( "github.com/goplus/llcppg/cl/internal/cltest" "github.com/goplus/llcppg/cl/internal/convert" "github.com/goplus/llcppg/cmd/gogensig/config" + "github.com/goplus/llcppg/cmd/gogensig/node" "github.com/goplus/llcppg/cmd/gogensig/unmarshal" llcppg "github.com/goplus/llcppg/config" "github.com/goplus/llgo/xtool/env" @@ -220,6 +221,8 @@ func testFrom(t *testing.T, dir string, gen bool, validateFunc func(t *testing.T t.Fatal(err) } + symbols := convert.NewProcessSymbol() + cvt, err := convert.NewConverter(&convert.Config{ PkgPath: ".", PkgName: cfg.Name, @@ -227,12 +230,22 @@ func testFrom(t *testing.T, dir string, gen bool, validateFunc func(t *testing.T OutputDir: outputDir, Pkg: convertPkg.File, FileMap: convertPkg.FileMap, - + NodeConv: node.NewNodeConverter( + &node.NodeConverterConfig{ + PkgName: cfg.Name, + // symbol table + FileMap: convertPkg.FileMap, + TypeMap: cfg.TypeMap, + TrimPrefixes: cfg.TrimPrefixes, + Symbols: symbols, + }, + ), TypeMap: cfg.TypeMap, Deps: cfg.Deps, TrimPrefixes: cfg.TrimPrefixes, Libs: cfg.Libs, KeepUnderScore: cfg.KeepUnderScore, + Symbols: symbols, }) if err != nil { t.Fatal(err) diff --git a/cl/internal/convert/package.go b/cl/internal/convert/package.go index 3dc6a3e5..4bc8a3a3 100644 --- a/cl/internal/convert/package.go +++ b/cl/internal/convert/package.go @@ -52,6 +52,7 @@ type PackageConfig struct { Name string // current package name OutputDir string ConvSym func(name *ast.Object, mangleName string) (goName string, err error) + Symbols *ProcessSymbol GenConf *gogen.Config TrimPrefixes []string LibCommand string // use to gen link command like $(pkg-config --libs xxx) @@ -67,12 +68,18 @@ func NewPackage(config *PackageConfig) (*Package, error) { EnableTypesalias: true, } } + var symbols *ProcessSymbol + if config.Symbols == nil { + symbols = NewProcessSymbol() + } else { + symbols = config.Symbols + } p := &Package{ p: gogen.NewPackage(config.PkgPath, config.Name, config.GenConf), conf: config, incompleteTypes: NewIncompleteTypes(), locMap: NewThirdTypeLoc(), - symbols: NewProcessSymbol(), + symbols: symbols, } // default have load llgo/c @@ -678,41 +685,45 @@ func (p *Package) createEnumItems(items []*ast.EnumItem, enumType types.Type) er return nil } -func (p *Package) NewMacro(macro *ast.Macro) error { - if !p.curFile.InCurPkg() { - return nil - } - +func (p *Package) NewMacro(macro *ast.Macro, goName string) error { // simple const macro define (#define NAME value) if len(macro.Tokens) == 2 && macro.Tokens[1].Token == ctoken.LITERAL { value := macro.Tokens[1].Lit defs := p.NewConstGroup() - node := Node{name: macro.Name, kind: Macro} - name, _, exist, err := p.RegisterNode(node, p.constName, p.lookupPub) - if err != nil { - return fmt.Errorf("NewMacro: %s fail: %w", macro.Name, err) - } - if exist { - if debugLog { - log.Printf("NewMacro: %s is processed\n", macro.Name) - } - return nil + + obj := p.lookupPub("", goName) + if obj != nil { + return fmt.Errorf("NewMacro: %s is already defined", macro.Name) } + + // node := Node{name: macro.Name, kind: Macro} + + // todo(zzy): remove this,current only to register name to ProcessSymbol,to keep other logic correct + // _, _, exist, err := p.RegisterNode(node, p.constName, p.lookupPub) + // if err != nil { + // return fmt.Errorf("NewMacro: %s fail: %w", macro.Name, err) + // } + // if exist { + // if debugLog { + // log.Printf("NewMacro: %s is processed\n", macro.Name) + // } + // return nil + // } if debugLog { - log.Printf("NewMacro: %s = %s\n", name, value) + log.Printf("NewMacro: %s = %s\n", goName, value) } if str, err := litToString(value); err == nil { - defs.New(str, nil, name) + defs.New(str, nil, goName) } else if _, err := litToUint(value); err == nil { defs.New(&goast.BasicLit{ Kind: token.INT, Value: value, - }, nil, name) + }, nil, goName) } else if _, err := litToFloat(value, 64); err == nil { defs.New(&goast.BasicLit{ Kind: token.FLOAT, Value: value, - }, nil, name) + }, nil, goName) } } return nil @@ -722,6 +733,13 @@ func (p *Package) NewConstGroup() *ConstGroup { return NewConstGroup(p.p, p.p.Types.Scope()) } +func (p *Package) SetGoFile(fileName string) error { + _, err := p.p.SetCurFile(fileName, true) + // todo(zzy):avoid mark every time + p.p.Unsafe().MarkForceUsed(p.p) + return err +} + type ConstGroup struct { defs *gogen.ConstDefs } @@ -959,10 +977,10 @@ func (it *IncompleteTypes) IterateIncomplete(fn func(*Incomplete) error) error { return nil } -type nodeKind int +type NodeKind int const ( - FuncDecl nodeKind = iota + 1 + FuncDecl NodeKind = iota + 1 TypeDecl TypedefDecl EnumTypeDecl @@ -972,7 +990,19 @@ const ( type Node struct { name string - kind nodeKind + kind NodeKind +} + +func NewNode(name string, kind NodeKind) Node { + return Node{name: name, kind: kind} +} + +func (n Node) Name() string { + return n.name +} + +func (n Node) Kind() NodeKind { + return n.kind } type ProcessSymbol struct { diff --git a/cl/internal/convert/package_bulitin_test.go b/cl/internal/convert/package_bulitin_test.go index 6af627be..cecb463e 100644 --- a/cl/internal/convert/package_bulitin_test.go +++ b/cl/internal/convert/package_bulitin_test.go @@ -105,7 +105,7 @@ func TestRedefPubName(t *testing.T) { Loc: &ast.Location{File: "temp.h"}, Name: "Bar", Tokens: []*ast.Token{{Token: ctoken.IDENT, Lit: "Bar"}, {Token: ctoken.LITERAL, Lit: "1"}}, - }) + }, "Bar") if err == nil { t.Fatal("expect a error") } diff --git a/cl/internal/convert/package_test.go b/cl/internal/convert/package_test.go index a5fefd97..21efdc3c 100644 --- a/cl/internal/convert/package_test.go +++ b/cl/internal/convert/package_test.go @@ -1165,14 +1165,16 @@ func TestRedef(t *testing.T) { Name: "MACRO_FOO", Tokens: []*ast.Token{{Token: token.IDENT, Lit: "MACRO_FOO"}, {Token: token.LITERAL, Lit: "1"}}, } - err = pkg.NewMacro(macro) + err = pkg.NewMacro(macro, macro.Name) if err != nil { t.Fatal("unexpect redefine err") } - err = pkg.NewMacro(macro) - if err != nil { - t.Fatal("unexpect redefine err") + // NOTE(zzy):in upper layer logic to avoid reprocess node after refactor. + // So here we expect a redefine error. + err = pkg.NewMacro(macro, macro.Name) + if err == nil { + t.Fatal("expect a redefine error") } var buf bytes.Buffer diff --git a/cmd/gogensig/gogensig.go b/cmd/gogensig/gogensig.go index 62b5941f..5b0348f1 100644 --- a/cmd/gogensig/gogensig.go +++ b/cmd/gogensig/gogensig.go @@ -27,6 +27,7 @@ import ( "github.com/goplus/llcppg/ast" "github.com/goplus/llcppg/cl" "github.com/goplus/llcppg/cmd/gogensig/config" + "github.com/goplus/llcppg/cmd/gogensig/node" "github.com/goplus/llcppg/cmd/gogensig/unmarshal" llcppg "github.com/goplus/llcppg/config" "github.com/qiniu/x/errors" @@ -79,6 +80,8 @@ func main() { symbTable, err := config.NewSymbolTable(symbFile) check(err) + symbols := cl.NewProcessSymbol() + pkg, err := cl.Convert(&cl.ConvConfig{ PkgName: conf.Name, ConvSym: func(name *ast.Object, mangleName string) (goName string, err error) { @@ -88,6 +91,16 @@ func main() { } return item.GoName, nil }, + NodeConv: node.NewNodeConverter( + &node.NodeConverterConfig{ + PkgName: conf.Name, + SymbTable: symbTable, + FileMap: convertPkg.FileMap, + TypeMap: conf.TypeMap, + TrimPrefixes: conf.TrimPrefixes, + Symbols: symbols, + }, + ), Pkg: convertPkg.File, FileMap: convertPkg.FileMap, TypeMap: conf.TypeMap, diff --git a/cmd/gogensig/node/node.go b/cmd/gogensig/node/node.go new file mode 100644 index 00000000..c0f5679f --- /dev/null +++ b/cmd/gogensig/node/node.go @@ -0,0 +1,158 @@ +package node + +import ( + "fmt" + "strings" + + "github.com/goplus/llcppg/_xtool/llcppsymg/tool/name" + "github.com/goplus/llcppg/ast" + "github.com/goplus/llcppg/cl" + "github.com/goplus/llcppg/cmd/gogensig/config" + llconfig "github.com/goplus/llcppg/config" +) + +// todo(zzy):a temp abstract,for cl/convert test & gogensig + +type NodeConverter struct { + symbols *cl.ProcessSymbol + conf *NodeConverterConfig +} + +type NodeConverterConfig struct { + PkgName string + SymbTable *config.SymbolTable + FileMap map[string]*llconfig.FileInfo + TrimPrefixes []string + TypeMap map[string]string + + // todo(zzy):remove this field + Symbols *cl.ProcessSymbol +} + +func NewNodeConverter(cfg *NodeConverterConfig) *NodeConverter { + var symbols *cl.ProcessSymbol + if cfg.Symbols == nil { + symbols = cl.NewProcessSymbol() + } else { + symbols = cfg.Symbols + } + return &NodeConverter{ + symbols: symbols, + conf: cfg, + } +} + +func (c *NodeConverter) ConvDecl(decl ast.Decl) (goName, goFile string, err error) { + return "", "", nil +} + +func (c *NodeConverter) ConvEnumItem(decl *ast.EnumTypeDecl, item *ast.EnumItem) (goName, goFile string, err error) { + return "", "", nil +} + +func (p *NodeConverter) ConvMacro(macro *ast.Macro) (goName, goFile string, err error) { + node := cl.NewNode(macro.Name, Macro) + goName, goFile, err = p.Register(macro.Loc, node, p.constName) + if err != nil { + return + } + return +} + +func (p *NodeConverter) Register(loc *ast.Location, node cl.Node, nameMethod NameMethod) (goName string, goFile string, err error) { + goFile, err = p.goFile(loc.File) + if err != nil { + return + } + pubName, exist := p.symbols.Lookup(node) + if exist { + return pubName, goFile, nil + } + goName, _ = p.GetUniqueName(node, nameMethod) + return goName, goFile, nil +} + +type NameMethod func(name string) string + +func (p *NodeConverter) goFile(file string) (string, error) { + info, ok := p.conf.FileMap[file] + if !ok { + var availableFiles []string + for f := range p.conf.FileMap { + availableFiles = append(availableFiles, f) + } + return "", fmt.Errorf("file %q not found in FileMap. Available files:\n%s", + file, strings.Join(availableFiles, "\n")) + } + switch info.FileType { + case llconfig.Inter: + return name.HeaderFileToGo(file), nil + case llconfig.Impl: + return p.conf.PkgName + "_autogen.go", nil + default: + return "", cl.ErrSkip + } +} + +// GetUniqueName generates a unique public name for a given node using the provided name transformation method. +// It ensures the generated name doesn't conflict with existing names by adding a numeric suffix if needed. +// +// Parameters: +// - node: The node containing the original name to be transformed +// - nameMethod: Function used to transform the original name (e.g., declName, constName) +// +// Returns: +// - pubName: The generated unique public name +// - changed: Whether the generated name differs from the original name +func (p *NodeConverter) GetUniqueName(node cl.Node, nameMethod NameMethod) (pubName string, changed bool) { + pubName = nameMethod(node.Name()) + uniquePubName := p.symbols.Register(node, pubName) + return uniquePubName, uniquePubName != node.Name() +} + +// which is define in llcppg.cfg/typeMap +func (p *NodeConverter) definedName(name string) (string, bool) { + definedName, ok := p.conf.TypeMap[name] + if ok { + if definedName == "" { + return name, true + } + return definedName, true + } + return name, false +} + +// transformName handles identifier name conversion following these rules: +// 1. First checks if the name exists in predefined mapping (in typeMap of llcppg.cfg) +// 2. If not in predefined mapping, applies the transform function +// 3. Before applying the transform function, removes specified prefixes (obtained via trimPrefixes) +// +// Parameters: +// - name: Original C/C++ identifier name +// - transform: Name transformation function (like names.PubName or names.ExportName) +// +// Returns: +// - Transformed identifier name +func (p *NodeConverter) transformName(cname string, transform NameMethod) string { + if definedName, ok := p.definedName(cname); ok { + return definedName + } + return transform(name.RemovePrefixedName(cname, p.conf.TrimPrefixes)) +} + +// func (p *NodeConverter) declName(cname string) string { +// return p.transformName(cname, name.PubName) +// } + +func (p *NodeConverter) constName(cname string) string { + return p.transformName(cname, name.ExportName) +} + +const ( + FuncDecl cl.NodeKind = iota + 1 + TypeDecl + TypedefDecl + EnumTypeDecl + EnumItem + Macro +)