| // 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"); |
| 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); |
| } |
| } |
| } |