// 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 ( "debug/dwarf"; "fmt"; "go/ast"; "go/doc"; "go/parser"; "go/scanner"; "os"; ) // A Cref refers to an expression of the form C.xxx in the AST. type Cref struct { Name string; Expr *ast.Expr; Context string; // "type", "expr", or "call" TypeName bool; // whether xxx is a C type name DebugType dwarf.Type; // the type of xxx } // A Prog collects information about a cgo program. type Prog struct { AST *ast.File; // parsed AST Preamble string; // C preamble (doc comment on import "C") Crefs []*Cref; } func openProg(name string) *Prog { p := new(Prog); var err os.Error; p.AST, err = parser.ParsePkgFile("", name, parser.ParseComments); 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); } fatal("parsing %s: %s", name, err); } // Find the import "C" line and get any extra C preamble. found := false; for _, d := range p.AST.Decls { d, ok := d.(*ast.GenDecl); if !ok { continue; } for _, s := range d.Specs { s, ok := s.(*ast.ImportSpec); if !ok { continue; } if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { continue; } found = true; if s.Name != nil { error(s.Path[0].Pos(), `cannot rename import "C"`); } if s.Doc != nil { p.Preamble += doc.CommentText(s.Doc) + "\n"; } else if len(d.Specs) == 1 && d.Doc != nil { p.Preamble += doc.CommentText(d.Doc) + "\n"; } } } if !found { error(noPos, `cannot find import "C"`); } // Accumulate pointers to uses of C.x. p.Crefs = make([]*Cref, 0, 8); walk(p.AST, p, "prog"); return p; } func walk(x interface{}, p *Prog, context string) { switch n := x.(type) { case *ast.Expr: 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.Value == "C" { i := len(p.Crefs); if i >= cap(p.Crefs) { new := make([]*Cref, 2*i); for j, v := range p.Crefs { new[j] = v; } p.Crefs = new; } p.Crefs = p.Crefs[0:i+1]; p.Crefs[i] = &Cref{ Name: sel.Sel.Value, Expr: n, Context: context }; break; } } walk(*n, p, context); // everything else just recurs default: error(noPos, "unexpected type %T in walk", x); panic(); case nil: // These are ordered and grouped to match ../../pkg/go/ast/ast.go case *ast.Field: walk(&n.Type, p, "type"); case *ast.BadExpr: case *ast.Ident: case *ast.Ellipsis: case *ast.BasicLit: case *ast.StringList: case *ast.FuncLit: walk(n.Type, p, "type"); walk(n.Body, p, "stmt"); case *ast.CompositeLit: walk(&n.Type, p, "type"); walk(n.Elts, p, "expr"); case *ast.ParenExpr: walk(&n.X, p, context); case *ast.SelectorExpr: walk(&n.X, p, "selector"); case *ast.IndexExpr: walk(&n.X, p, "expr"); walk(&n.Index, p, "expr"); if n.End != nil { walk(&n.End, p, "expr"); } case *ast.TypeAssertExpr: walk(&n.X, p, "expr"); walk(&n.Type, p, "type"); case *ast.CallExpr: walk(&n.Fun, p, "call"); walk(n.Args, p, "expr"); case *ast.StarExpr: walk(&n.X, p, context); case *ast.UnaryExpr: walk(&n.X, p, "expr"); case *ast.BinaryExpr: walk(&n.X, p, "expr"); walk(&n.Y, p, "expr"); case *ast.KeyValueExpr: walk(&n.Key, p, "expr"); walk(&n.Value, p, "expr"); case *ast.ArrayType: walk(&n.Len, p, "expr"); walk(&n.Elt, p, "type"); case *ast.StructType: walk(n.Fields, p, "field"); case *ast.FuncType: walk(n.Params, p, "field"); walk(n.Results, p, "field"); case *ast.InterfaceType: walk(n.Methods, p, "field"); case *ast.MapType: walk(&n.Key, p, "type"); walk(&n.Value, p, "type"); case *ast.ChanType: walk(&n.Value, p, "type"); case *ast.BadStmt: case *ast.DeclStmt: walk(n.Decl, p, "decl"); case *ast.EmptyStmt: case *ast.LabeledStmt: walk(n.Stmt, p, "stmt"); case *ast.ExprStmt: walk(&n.X, p, "expr"); case *ast.IncDecStmt: walk(&n.X, p, "expr"); case *ast.AssignStmt: walk(n.Lhs, p, "expr"); walk(n.Rhs, p, "expr"); case *ast.GoStmt: walk(&n.Call, p, "expr"); case *ast.DeferStmt: walk(&n.Call, p, "expr"); case *ast.ReturnStmt: walk(n.Results, p, "expr"); case *ast.BranchStmt: case *ast.BlockStmt: walk(n.List, p, "stmt"); case *ast.IfStmt: walk(n.Init, p, "stmt"); walk(&n.Cond, p, "expr"); walk(n.Body, p, "stmt"); walk(n.Else, p, "stmt"); case *ast.CaseClause: walk(n.Values, p, "expr"); walk(n.Body, p, "stmt"); case *ast.SwitchStmt: walk(n.Init, p, "stmt"); walk(&n.Tag, p, "expr"); walk(n.Body, p, "stmt"); case *ast.TypeCaseClause: walk(n.Types, p, "type"); walk(n.Body, p, "stmt"); case *ast.TypeSwitchStmt: walk(n.Init, p, "stmt"); walk(n.Assign, p, "stmt"); walk(n.Body, p, "stmt"); case *ast.CommClause: walk(n.Lhs, p, "expr"); walk(n.Rhs, p, "expr"); walk(n.Body, p, "stmt"); case *ast.SelectStmt: walk(n.Body, p, "stmt"); case *ast.ForStmt: walk(n.Init, p, "stmt"); walk(&n.Cond, p, "expr"); walk(n.Post, p, "stmt"); walk(n.Body, p, "stmt"); case *ast.RangeStmt: walk(&n.Key, p, "expr"); walk(&n.Value, p, "expr"); walk(&n.X, p, "expr"); walk(n.Body, p, "stmt"); case *ast.ImportSpec: case *ast.ValueSpec: walk(&n.Type, p, "type"); walk(n.Values, p, "expr"); case *ast.TypeSpec: walk(&n.Type, p, "type"); case *ast.BadDecl: case *ast.GenDecl: walk(n.Specs, p, "spec"); case *ast.FuncDecl: if n.Recv != nil { walk(n.Recv, p, "field"); } walk(n.Type, p, "type"); walk(n.Body, p, "stmt"); case *ast.File: walk(n.Decls, p, "decl"); case *ast.Package: for _, f := range n.Files { walk(f, p, "file"); } case []ast.Decl: for _, d := range n { walk(d, p, context); } case []ast.Expr: for i := range n { walk(&n[i], p, context); } case []*ast.Field: for _, f := range n { walk(f, p, context); } case []ast.Stmt: for _, s := range n { walk(s, p, context); } case []ast.Spec: for _, s := range n { walk(s, p, context); } } }