// 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", 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; PtrSize int64; GccOptions []string; } // 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; } // 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) *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); } p.Package = p.AST.Name.Value; // 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 || len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { d.Specs[ws] = spec; ws++; continue; } sawC = 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 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. 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"); 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"); 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.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) } } }