diff options
Diffstat (limited to 'src/cmd/gotype/gotype.go')
-rw-r--r-- | src/cmd/gotype/gotype.go | 198 |
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) +} |