+++ /dev/null
-package main
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/parser"
- "go/scanner"
- "go/token"
- "text/template"
-)
-
-var tmplDecl = template.Must(template.New("").Parse(`` +
- `package p; {{ . }}`))
-
-var tmplExprs = template.Must(template.New("").Parse(`` +
- `package p; var _ = []interface{}{ {{ . }}, }`))
-
-var tmplStmts = template.Must(template.New("").Parse(`` +
- `package p; func _() { {{ . }} }`))
-
-var tmplType = template.Must(template.New("").Parse(`` +
- `package p; var _ {{ . }}`))
-
-var tmplValSpec = template.Must(template.New("").Parse(`` +
- `package p; var {{ . }}`))
-
-func execTmpl(tmpl *template.Template, src string) string {
- var buf bytes.Buffer
- if err := tmpl.Execute(&buf, src); err != nil {
- panic(err)
- }
- return buf.String()
-}
-
-func noBadNodes(node ast.Node) bool {
- any := false
- ast.Inspect(node, func(n ast.Node) bool {
- if any {
- return false
- }
- switch n.(type) {
- case *ast.BadExpr, *ast.BadDecl:
- any = true
- }
- return true
- })
- return !any
-}
-
-func parseType(fset *token.FileSet, src string) (ast.Expr, *ast.File, error) {
- asType := execTmpl(tmplType, src)
- f, err := parser.ParseFile(fset, "", asType, 0)
- if err != nil {
- return nil, nil, err
- }
- vs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
- return vs.Type, f, nil
-}
-
-// parseDetectingNode tries its best to parse the ast.Node contained in src, as
-// one of: *ast.File, ast.Decl, ast.Expr, ast.Stmt, *ast.ValueSpec.
-// It also returns the *ast.File used for the parsing, so that the returned node
-// can be easily type-checked.
-func parseDetectingNode(fset *token.FileSet, src string) (interface{}, error) {
- file := fset.AddFile("", fset.Base(), len(src))
- scan := scanner.Scanner{}
- scan.Init(file, []byte(src), nil, 0)
- if _, tok, _ := scan.Scan(); tok == token.EOF {
- return nil, fmt.Errorf("empty source code")
- }
- var mainErr error
-
- // first try as a whole file
- if f, err := parser.ParseFile(fset, "", src, 0); err == nil && noBadNodes(f) {
- return f, nil
- }
-
- // then as a single declaration, or many
- asDecl := execTmpl(tmplDecl, src)
- if f, err := parser.ParseFile(fset, "", asDecl, 0); err == nil && noBadNodes(f) {
- if len(f.Decls) == 1 {
- return f.Decls[0], nil
- }
- return f, nil
- }
-
- // then as value expressions
- asExprs := execTmpl(tmplExprs, src)
- if f, err := parser.ParseFile(fset, "", asExprs, 0); err == nil && noBadNodes(f) {
- vs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
- cl := vs.Values[0].(*ast.CompositeLit)
- if len(cl.Elts) == 1 {
- return cl.Elts[0], nil
- }
- return cl.Elts, nil
- }
-
- // then try as statements
- asStmts := execTmpl(tmplStmts, src)
- if f, err := parser.ParseFile(fset, "", asStmts, 0); err == nil && noBadNodes(f) {
- bl := f.Decls[0].(*ast.FuncDecl).Body
- if len(bl.List) == 1 {
- return bl.List[0], nil
- }
- return bl.List, nil
- } else {
- // Statements is what covers most cases, so it will give
- // the best overall error message. Show positions
- // relative to where the user's code is put in the
- // template.
- mainErr = err
- }
-
- // type expressions not yet picked up, for e.g. chans and interfaces
- if typ, f, err := parseType(fset, src); err == nil && noBadNodes(f) {
- return typ, nil
- }
-
- // value specs
- asValSpec := execTmpl(tmplValSpec, src)
- if f, err := parser.ParseFile(fset, "", asValSpec, 0); err == nil && noBadNodes(f) {
- vs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
- return vs, nil
- }
- return nil, mainErr
-}