Skip to content

fix: compatible unmarshal historical version of AST json #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions lang/golang/parser/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import (
func (p *GoParser) parseFile(ctx *fileContext, f *ast.File) error {
cont := true
ast.Inspect(f, func(node ast.Node) bool {
// defer func() {
// if r := recover(); r != nil {
// fmt.Fprintf(os.Stderr, "panic: %v in %s:%d\n", r, ctx.filePath, ctx.fset.Position(node.Pos()).Line)
// cont = false
// return
// }
// }()
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "panic: %v in %s:%d\n", r, ctx.filePath, ctx.fset.Position(node.Pos()).Line)
cont = false
return
}
}()
if funcDecl, ok := node.(*ast.FuncDecl); ok {
// parse funcs
_, ct := p.parseFunc(ctx, funcDecl)
Expand Down
6 changes: 3 additions & 3 deletions lang/golang/parser/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ func isPkgScope(scope *types.Scope) bool {
func getTypeKind(n ast.Expr) TypeKind {
switch n.(type) {
case *ast.StructType:
return "struct"
return TypeKindStruct
case *ast.InterfaceType:
return "interface"
return TypeKindInterface
default:
return "typedef"
return TypeKindTypedef
}
}

Expand Down
7 changes: 7 additions & 0 deletions lang/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type ParseOptions struct {
// Language of the repo
Verbose bool
collect.CollectOption
// specify the repo id
RepoID string
}

func Parse(ctx context.Context, uri string, args ParseOptions) ([]byte, error) {
Expand Down Expand Up @@ -78,6 +80,11 @@ func Parse(ctx context.Context, uri string, args ParseOptions) ([]byte, error) {
return nil, err
}
log.Info("all symbols collected, start writing to stdout...\n")

if args.RepoID != "" {
repo.Name = args.RepoID
}

out, err := json.Marshal(repo)
if err != nil {
log.Error("Failed to marshal repository: %v\n", err)
Expand Down
52 changes: 51 additions & 1 deletion lang/uniast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package uniast

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)

Expand Down Expand Up @@ -103,6 +105,28 @@ type Import struct {
Path string
}

func (i *Import) UnmarshalJSON(data []byte) error {
if len(data) >= 2 && data[0] == '"' && data[len(data)-1] == '"' {
v, e := strconv.Unquote(string(data))
if e != nil {
return e
}
i.Path = v
return nil
} else {
var ii importObj
err := json.Unmarshal(data, &ii)
if err != nil {
return err
}
i.Alias = ii.Alias
i.Path = ii.Path
return nil
}
}

type importObj Import

func NewImport(alias *string, path string) Import {
return Import{
Alias: alias,
Expand Down Expand Up @@ -448,6 +472,31 @@ type FileLine struct {

type TypeKind string

const (
TypeKindStruct TypeKind = "struct"
TypeKindInterface TypeKind = "interface"
TypeKindTypedef TypeKind = "typedef"
TypeKindEnum TypeKind = "enum"
)

func (t *TypeKind) UnmarshalJSON(data []byte) error {
if len(data) >= 2 && data[0] == '"' && data[len(data)-1] == '"' {
*t = TypeKind(data[1 : len(data)-1])
return nil
}

// 兼容历史go ast
switch string(data) {
case "0":
*t = TypeKindStruct
case "1":
*t = TypeKindInterface
default:
*t = TypeKindTypedef
}
return nil
}

// const (
// TypeKindStruct = 0 // type struct
// TypeKindInterface = 1 // type interface
Expand All @@ -459,7 +508,8 @@ type TypeKind string
type Type struct {
Exported bool // if the struct is exported

TypeKind // type Kind: Struct / Interface / Typedef
TypeKind TypeKind // type Kind: Struct / Interface / Typedef

Identity // unique id in a repo
FileLine
Content string // struct declaration content
Expand Down
55 changes: 29 additions & 26 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,48 +51,56 @@ Action:
Language:
rust for rust codes
go for golang codes
URI:
for action parse: the directory path of the repo
for action write: the file path of the UniAST for writer
`

func main() {
flags := flag.NewFlagSet("abcoder", flag.ExitOnError)

flagHelp := flags.Bool("h", false, "Show help message.")

flagVerbose := flags.Bool("verbose", false, "Verbose mode.")

flagOutput := flags.String("o", "", "Output path.")

var opts lang.ParseOptions
flags.BoolVar(&opts.LoadExternalSymbol, "load-external-symbol", false, "load external symbols into results")
flags.BoolVar(&opts.NoNeedComment, "no-need-comment", false, "do not need comment (only works for Go now)")
flags.BoolVar(&opts.NeedTest, "need-test", false, "need parse test files (only works for Go now)")
flags.Var((*StringArray)(&opts.Excludes), "exclude", "exclude files or directories, support multiple values")
flags.StringVar(&opts.RepoID, "repo-id", "", "specify the repo id")
flagLsp := flags.String("lsp", "", "Specify the language server path.")

var wopts lang.WriteOptions
flags.StringVar(&wopts.Compiler, "compiler", "", "destination compiler path.")

flags.Usage = func() {
fmt.Fprintf(os.Stderr, Usage)
fmt.Fprint(os.Stderr, Usage)
fmt.Fprintf(os.Stderr, "Flags:\n")
flags.PrintDefaults()
}

if len(os.Args) < 4 {
fmt.Fprintf(os.Stderr, Usage)
// call flags.Usage()
flags.Usage()
os.Exit(1)
}

action := strings.ToLower(os.Args[1])
language := uniast.NewLanguage(os.Args[2])
if language == uniast.Unknown {
fmt.Fprintf(os.Stderr, "unsupported language: %s\n", os.Args[2])
os.Exit(1)
}

uri := os.Args[3]

flagVerbose := flags.Bool("verbose", false, "Verbose mode.")

flagOutput := flags.String("o", "", "Output path.")
flags.Parse(os.Args[4:])
if flagHelp != nil && *flagHelp {
flags.Usage()
os.Exit(0)
}

switch action {
case "parse":
var opts lang.ParseOptions

flags.BoolVar(&opts.LoadExternalSymbol, "load-external-symbol", false, "load external symbols into results")
flags.BoolVar(&opts.NoNeedComment, "no-need-comment", false, "do not need comment (only works for Go now)")
flags.BoolVar(&opts.NeedTest, "need-test", false, "need parse test files (only works for Go now)")
flags.Var((*StringArray)(&opts.Excludes), "exclude", "exclude files or directories, support multiple values")
flagLsp := flags.String("lsp", "", "Specify the language server path.")

flags.Parse(os.Args[4:])
if flagVerbose != nil && *flagVerbose {
log.SetLogLevel(log.DebugLevel)
opts.Verbose = true
Expand Down Expand Up @@ -123,21 +131,16 @@ func main() {
os.Exit(1)
}

var opts lang.WriteOptions
flags.StringVar(&opts.Compiler, "compiler", "", "destination compiler path.")

flags.Parse(os.Args[4:])

if flagVerbose != nil && *flagVerbose {
log.SetLogLevel(log.DebugLevel)
}
if flagOutput != nil && *flagOutput != "" {
opts.OutputDir = *flagOutput
wopts.OutputDir = *flagOutput
} else {
opts.OutputDir = filepath.Base(repo.Name)
wopts.OutputDir = filepath.Base(repo.Name)
}

if err := lang.Write(context.Background(), repo, opts); err != nil {
if err := lang.Write(context.Background(), repo, wopts); err != nil {
log.Error("Failed to write: %v\n", err)
os.Exit(1)
}
Expand Down