summaryrefslogtreecommitdiff
path: root/src/cmd/gotype/gotype.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gotype/gotype.go')
-rw-r--r--src/cmd/gotype/gotype.go198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/cmd/gotype/gotype.go b/src/cmd/gotype/gotype.go
new file mode 100644
index 000000000..568467322
--- /dev/null
+++ b/src/cmd/gotype/gotype.go
@@ -0,0 +1,198 @@
+// Copyright 2011 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.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "go/types"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+
+var (
+ // main operation modes
+ pkgName = flag.String("p", "", "process only those files in package pkgName")
+ recursive = flag.Bool("r", false, "recursively process subdirectories")
+ verbose = flag.Bool("v", false, "verbose mode")
+
+ // debugging support
+ printTrace = flag.Bool("trace", false, "print parse trace")
+ printAST = flag.Bool("ast", false, "print AST")
+)
+
+
+var exitCode = 0
+
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+
+func report(err os.Error) {
+ scanner.PrintError(os.Stderr, err)
+ exitCode = 2
+}
+
+
+// parse returns the AST for the Go source src.
+// The filename is for error reporting only.
+// The result is nil if there were errors or if
+// the file does not belong to the -p package.
+func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
+ if *verbose {
+ fmt.Println(filename)
+ }
+
+ // ignore files with different package name
+ if *pkgName != "" {
+ file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly)
+ if err != nil {
+ report(err)
+ return nil
+ }
+ if file.Name.Name != *pkgName {
+ if *verbose {
+ fmt.Printf("\tignored (package %s)\n", file.Name.Name)
+ }
+ return nil
+ }
+ }
+
+ // parse entire file
+ mode := parser.DeclarationErrors
+ if *printTrace {
+ mode |= parser.Trace
+ }
+ file, err := parser.ParseFile(fset, filename, src, mode)
+ if err != nil {
+ report(err)
+ return nil
+ }
+ if *printAST {
+ ast.Print(fset, file)
+ }
+
+ return file
+}
+
+
+func parseStdin(fset *token.FileSet) (files map[string]*ast.File) {
+ files = make(map[string]*ast.File)
+ src, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ report(err)
+ return
+ }
+ const filename = "<standard input>"
+ if file := parse(fset, filename, src); file != nil {
+ files[filename] = file
+ }
+ return
+}
+
+
+func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.File) {
+ files = make(map[string]*ast.File)
+ for _, filename := range filenames {
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ report(err)
+ continue
+ }
+ if file := parse(fset, filename, src); file != nil {
+ if files[filename] != nil {
+ report(os.ErrorString(fmt.Sprintf("%q: duplicate file", filename)))
+ continue
+ }
+ files[filename] = file
+ }
+ }
+ return
+}
+
+
+func isGoFilename(filename string) bool {
+ // ignore non-Go files
+ return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go")
+}
+
+
+func processDirectory(dirname string) {
+ f, err := os.Open(dirname)
+ if err != nil {
+ report(err)
+ return
+ }
+ filenames, err := f.Readdirnames(-1)
+ f.Close()
+ if err != nil {
+ report(err)
+ // continue since filenames may not be empty
+ }
+ for i, filename := range filenames {
+ filenames[i] = filepath.Join(dirname, filename)
+ }
+ processFiles(filenames, false)
+}
+
+
+func processFiles(filenames []string, allFiles bool) {
+ i := 0
+ for _, filename := range filenames {
+ switch info, err := os.Stat(filename); {
+ case err != nil:
+ report(err)
+ case info.IsRegular():
+ if allFiles || isGoFilename(info.Name) {
+ filenames[i] = filename
+ i++
+ }
+ case info.IsDirectory():
+ if allFiles || *recursive {
+ processDirectory(filename)
+ }
+ }
+ }
+ fset := token.NewFileSet()
+ processPackage(fset, parseFiles(fset, filenames[0:i]))
+}
+
+
+func processPackage(fset *token.FileSet, files map[string]*ast.File) {
+ // make a package (resolve all identifiers)
+ pkg, err := ast.NewPackage(fset, files, types.GcImporter, types.Universe)
+ if err != nil {
+ report(err)
+ return
+ }
+ // TODO(gri): typecheck package
+ _ = pkg
+}
+
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() == 0 {
+ fset := token.NewFileSet()
+ processPackage(fset, parseStdin(fset))
+ } else {
+ processFiles(flag.Args(), true)
+ }
+
+ os.Exit(exitCode)
+}