123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Parse input AST and prepare Prog structure.
- package main
- import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/scanner"
- "go/token"
- "os"
- "path/filepath"
- "strings"
- )
- func parse(name string, flags parser.Mode) *ast.File {
- ast1, err := parser.ParseFile(fset, name, nil, flags)
- if err != nil {
- if list, ok := err.(scanner.ErrorList); ok {
- // If err is a scanner.ErrorList, its String will print just
- // the first error and then (+n more errors).
- // Instead, turn it into a new Error that will return
- // details for all the errors.
- for _, e := range list {
- fmt.Fprintln(os.Stderr, e)
- }
- os.Exit(2)
- }
- fatalf("parsing %s: %s", name, err)
- }
- return ast1
- }
- func sourceLine(n ast.Node) int {
- return fset.Position(n.Pos()).Line
- }
- // ReadGo populates f with information learned from reading the
- // Go source file with the given file name. It gathers the C preamble
- // attached to the import "C" comment, a list of references to C.xxx,
- // a list of exported functions, and the actual AST, to be rewritten and
- // printed.
- func (f *File) ReadGo(name string) {
- // Create absolute path for file, so that it will be used in error
- // messages and recorded in debug line number information.
- // This matches the rest of the toolchain. See golang.org/issue/5122.
- if aname, err := filepath.Abs(name); err == nil {
- name = aname
- }
- // Two different parses: once with comments, once without.
- // The printer is not good enough at printing comments in the
- // right place when we start editing the AST behind its back,
- // so we use ast1 to look for the doc comments on import "C"
- // and on exported functions, and we use ast2 for translating
- // and reprinting.
- ast1 := parse(name, parser.ParseComments)
- ast2 := parse(name, 0)
- f.Package = ast1.Name.Name
- f.Name = make(map[string]*Name)
- // In ast1, find the import "C" line and get any extra C preamble.
- sawC := false
- for _, decl := range ast1.Decls {
- d, ok := decl.(*ast.GenDecl)
- if !ok {
- continue
- }
- for _, spec := range d.Specs {
- s, ok := spec.(*ast.ImportSpec)
- if !ok || string(s.Path.Value) != `"C"` {
- continue
- }
- sawC = true
- if s.Name != nil {
- error_(s.Path.Pos(), `cannot rename import "C"`)
- }
- cg := s.Doc
- if cg == nil && len(d.Specs) == 1 {
- cg = d.Doc
- }
- if cg != nil {
- f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
- f.Preamble += commentText(cg) + "\n"
- }
- }
- }
- if !sawC {
- error_(token.NoPos, `cannot find import "C"`)
- }
- // In ast2, strip the import "C" line.
- w := 0
- for _, decl := range ast2.Decls {
- d, ok := decl.(*ast.GenDecl)
- if !ok {
- ast2.Decls[w] = decl
- w++
- continue
- }
- ws := 0
- for _, spec := range d.Specs {
- s, ok := spec.(*ast.ImportSpec)
- if !ok || string(s.Path.Value) != `"C"` {
- d.Specs[ws] = spec
- ws++
- }
- }
- if ws == 0 {
- continue
- }
- d.Specs = d.Specs[0:ws]
- ast2.Decls[w] = d
- w++
- }
- ast2.Decls = ast2.Decls[0:w]
- // Accumulate pointers to uses of C.x.
- if f.Ref == nil {
- f.Ref = make([]*Ref, 0, 8)
- }
- f.walk(ast2, "prog", (*File).saveRef)
- // Accumulate exported functions.
- // The comments are only on ast1 but we need to
- // save the function bodies from ast2.
- // The first walk fills in ExpFunc, and the
- // second walk changes the entries to
- // refer to ast2 instead.
- f.walk(ast1, "prog", (*File).saveExport)
- f.walk(ast2, "prog", (*File).saveExport2)
- f.Comments = ast1.Comments
- f.AST = ast2
- }
- // Like ast.CommentGroup's Text method but preserves
- // leading blank lines, so that line numbers line up.
- func commentText(g *ast.CommentGroup) string {
- if g == nil {
- return ""
- }
- var pieces []string
- for _, com := range g.List {
- c := string(com.Text)
- // Remove comment markers.
- // The parser has given us exactly the comment text.
- switch c[1] {
- case '/':
- //-style comment (no newline at the end)
- c = c[2:] + "\n"
- case '*':
- /*-style comment */
- c = c[2 : len(c)-2]
- }
- pieces = append(pieces, c)
- }
- return strings.Join(pieces, "")
- }
- // Save references to C.xxx for later processing.
- func (f *File) saveRef(x interface{}, context string) {
- n, ok := x.(*ast.Expr)
- if !ok {
- return
- }
- if sel, ok := (*n).(*ast.SelectorExpr); ok {
- // For now, assume that the only instance of capital C is
- // when used as the imported package identifier.
- // The parser should take care of scoping in the future,
- // so that we will be able to distinguish a "top-level C"
- // from a local C.
- if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" {
- if context == "as2" {
- context = "expr"
- }
- if context == "embed-type" {
- error_(sel.Pos(), "cannot embed C type")
- }
- goname := sel.Sel.Name
- if goname == "errno" {
- error_(sel.Pos(), "cannot refer to errno directly; see documentation")
- return
- }
- if goname == "_CMalloc" {
- error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
- return
- }
- if goname == "malloc" {
- goname = "_CMalloc"
- }
- name := f.Name[goname]
- if name == nil {
- name = &Name{
- Go: goname,
- }
- f.Name[goname] = name
- }
- f.Ref = append(f.Ref, &Ref{
- Name: name,
- Expr: n,
- Context: context,
- })
- return
- }
- }
- }
- // If a function should be exported add it to ExpFunc.
- func (f *File) saveExport(x interface{}, context string) {
- n, ok := x.(*ast.FuncDecl)
- if !ok {
- return
- }
- if n.Doc == nil {
- return
- }
- for _, c := range n.Doc.List {
- if !strings.HasPrefix(string(c.Text), "//export ") {
- continue
- }
- name := strings.TrimSpace(string(c.Text[9:]))
- if name == "" {
- error_(c.Pos(), "export missing name")
- }
- if name != n.Name.Name {
- error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
- }
- f.ExpFunc = append(f.ExpFunc, &ExpFunc{
- Func: n,
- ExpName: name,
- })
- break
- }
- }
- // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
- func (f *File) saveExport2(x interface{}, context string) {
- n, ok := x.(*ast.FuncDecl)
- if !ok {
- return
- }
- for _, exp := range f.ExpFunc {
- if exp.Func.Name.Name == n.Name.Name {
- exp.Func = n
- break
- }
- }
- }
- // walk walks the AST x, calling visit(f, x, context) for each node.
- func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) {
- visit(f, x, context)
- switch n := x.(type) {
- case *ast.Expr:
- f.walk(*n, context, visit)
- // everything else just recurs
- default:
- error_(token.NoPos, "unexpected type %T in walk", x, visit)
- panic("unexpected type")
- case nil:
- // These are ordered and grouped to match ../../go/ast/ast.go
- case *ast.Field:
- if len(n.Names) == 0 && context == "field" {
- f.walk(&n.Type, "embed-type", visit)
- } else {
- f.walk(&n.Type, "type", visit)
- }
- case *ast.FieldList:
- for _, field := range n.List {
- f.walk(field, context, visit)
- }
- case *ast.BadExpr:
- case *ast.Ident:
- case *ast.Ellipsis:
- case *ast.BasicLit:
- case *ast.FuncLit:
- f.walk(n.Type, "type", visit)
- f.walk(n.Body, "stmt", visit)
- case *ast.CompositeLit:
- f.walk(&n.Type, "type", visit)
- f.walk(n.Elts, "expr", visit)
- case *ast.ParenExpr:
- f.walk(&n.X, context, visit)
- case *ast.SelectorExpr:
- f.walk(&n.X, "selector", visit)
- case *ast.IndexExpr:
- f.walk(&n.X, "expr", visit)
- f.walk(&n.Index, "expr", visit)
- case *ast.SliceExpr:
- f.walk(&n.X, "expr", visit)
- if n.Low != nil {
- f.walk(&n.Low, "expr", visit)
- }
- if n.High != nil {
- f.walk(&n.High, "expr", visit)
- }
- if n.Max != nil {
- f.walk(&n.Max, "expr", visit)
- }
- case *ast.TypeAssertExpr:
- f.walk(&n.X, "expr", visit)
- f.walk(&n.Type, "type", visit)
- case *ast.CallExpr:
- if context == "as2" {
- f.walk(&n.Fun, "call2", visit)
- } else {
- f.walk(&n.Fun, "call", visit)
- }
- f.walk(n.Args, "expr", visit)
- case *ast.StarExpr:
- f.walk(&n.X, context, visit)
- case *ast.UnaryExpr:
- f.walk(&n.X, "expr", visit)
- case *ast.BinaryExpr:
- f.walk(&n.X, "expr", visit)
- f.walk(&n.Y, "expr", visit)
- case *ast.KeyValueExpr:
- f.walk(&n.Key, "expr", visit)
- f.walk(&n.Value, "expr", visit)
- case *ast.ArrayType:
- f.walk(&n.Len, "expr", visit)
- f.walk(&n.Elt, "type", visit)
- case *ast.StructType:
- f.walk(n.Fields, "field", visit)
- case *ast.FuncType:
- f.walk(n.Params, "param", visit)
- if n.Results != nil {
- f.walk(n.Results, "param", visit)
- }
- case *ast.InterfaceType:
- f.walk(n.Methods, "field", visit)
- case *ast.MapType:
- f.walk(&n.Key, "type", visit)
- f.walk(&n.Value, "type", visit)
- case *ast.ChanType:
- f.walk(&n.Value, "type", visit)
- case *ast.BadStmt:
- case *ast.DeclStmt:
- f.walk(n.Decl, "decl", visit)
- case *ast.EmptyStmt:
- case *ast.LabeledStmt:
- f.walk(n.Stmt, "stmt", visit)
- case *ast.ExprStmt:
- f.walk(&n.X, "expr", visit)
- case *ast.SendStmt:
- f.walk(&n.Chan, "expr", visit)
- f.walk(&n.Value, "expr", visit)
- case *ast.IncDecStmt:
- f.walk(&n.X, "expr", visit)
- case *ast.AssignStmt:
- f.walk(n.Lhs, "expr", visit)
- if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
- f.walk(n.Rhs, "as2", visit)
- } else {
- f.walk(n.Rhs, "expr", visit)
- }
- case *ast.GoStmt:
- f.walk(n.Call, "expr", visit)
- case *ast.DeferStmt:
- f.walk(n.Call, "expr", visit)
- case *ast.ReturnStmt:
- f.walk(n.Results, "expr", visit)
- case *ast.BranchStmt:
- case *ast.BlockStmt:
- f.walk(n.List, context, visit)
- case *ast.IfStmt:
- f.walk(n.Init, "stmt", visit)
- f.walk(&n.Cond, "expr", visit)
- f.walk(n.Body, "stmt", visit)
- f.walk(n.Else, "stmt", visit)
- case *ast.CaseClause:
- if context == "typeswitch" {
- context = "type"
- } else {
- context = "expr"
- }
- f.walk(n.List, context, visit)
- f.walk(n.Body, "stmt", visit)
- case *ast.SwitchStmt:
- f.walk(n.Init, "stmt", visit)
- f.walk(&n.Tag, "expr", visit)
- f.walk(n.Body, "switch", visit)
- case *ast.TypeSwitchStmt:
- f.walk(n.Init, "stmt", visit)
- f.walk(n.Assign, "stmt", visit)
- f.walk(n.Body, "typeswitch", visit)
- case *ast.CommClause:
- f.walk(n.Comm, "stmt", visit)
- f.walk(n.Body, "stmt", visit)
- case *ast.SelectStmt:
- f.walk(n.Body, "stmt", visit)
- case *ast.ForStmt:
- f.walk(n.Init, "stmt", visit)
- f.walk(&n.Cond, "expr", visit)
- f.walk(n.Post, "stmt", visit)
- f.walk(n.Body, "stmt", visit)
- case *ast.RangeStmt:
- f.walk(&n.Key, "expr", visit)
- f.walk(&n.Value, "expr", visit)
- f.walk(&n.X, "expr", visit)
- f.walk(n.Body, "stmt", visit)
- case *ast.ImportSpec:
- case *ast.ValueSpec:
- f.walk(&n.Type, "type", visit)
- f.walk(n.Values, "expr", visit)
- case *ast.TypeSpec:
- f.walk(&n.Type, "type", visit)
- case *ast.BadDecl:
- case *ast.GenDecl:
- f.walk(n.Specs, "spec", visit)
- case *ast.FuncDecl:
- if n.Recv != nil {
- f.walk(n.Recv, "param", visit)
- }
- f.walk(n.Type, "type", visit)
- if n.Body != nil {
- f.walk(n.Body, "stmt", visit)
- }
- case *ast.File:
- f.walk(n.Decls, "decl", visit)
- case *ast.Package:
- for _, file := range n.Files {
- f.walk(file, "file", visit)
- }
- case []ast.Decl:
- for _, d := range n {
- f.walk(d, context, visit)
- }
- case []ast.Expr:
- for i := range n {
- f.walk(&n[i], context, visit)
- }
- case []ast.Stmt:
- for _, s := range n {
- f.walk(s, context, visit)
- }
- case []ast.Spec:
- for _, s := range n {
- f.walk(s, context, visit)
- }
- }
- }
|