// 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/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", "const", or "call" TypeName bool // whether xxx is a C type name Type *Type // the type of xxx FuncType *FuncType } // 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") PackagePath string Package string Crefs []*Cref Typedef map[string]ast.Expr Vardef map[string]*Type Funcdef map[string]*FuncType Enumdef map[string]int64 Constdef map[string]string PtrSize int64 GccOptions []string OutDefs map[string]bool } // A Type collects information about a type in both the C and Go worlds. type Type struct { Size int64 Align int64 C string Go ast.Expr EnumValues map[string]int64 } // A FuncType collects information about a function type in both the C and Go worlds. type FuncType struct { Params []*Type Result *Type Go *ast.FuncType } func openProg(name string, p *Prog) { var err os.Error p.AST, err = parser.ParseFile(name, nil, nil, 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) } p.Package = p.AST.Name.Name() // Find the import "C" line and get any extra C preamble. // Delete the import "C" line along the way. sawC := false w := 0 for _, decl := range p.AST.Decls { d, ok := decl.(*ast.GenDecl) if !ok { p.AST.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++ continue } sawC = true if s.Name != nil { error(s.Path.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 ws == 0 { continue } d.Specs = d.Specs[0:ws] p.AST.Decls[w] = d w++ } p.AST.Decls = p.AST.Decls[0:w] if !sawC { error(noPos, `cannot find import "C"`) } // Accumulate pointers to uses of C.x. if p.Crefs == nil { p.Crefs = make([]*Cref, 0, 8) } walk(p.AST, p, "prog") } 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.Name() == "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.Name(), Expr: n, Context: context, } break } } walk(*n, p, context) // everything else just recurs default: error(noPos, "unexpected type %T in walk", x) panic("unexpected type") case nil: // These are ordered and grouped to match ../../pkg/go/ast/ast.go case *ast.Field: walk(&n.Type, p, "type") case *ast.FieldList: for _, f := range n.List { walk(f, p, context) } case *ast.BadExpr: case *ast.Ident: case *ast.Ellipsis: case *ast.BasicLit: 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") case *ast.SliceExpr: 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") if n.Results != nil { 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") if n.Body != nil { 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.Stmt: for _, s := range n { walk(s, p, context) } case []ast.Spec: for _, s := range n { walk(s, p, context) } } }