Exemple #1
0
func stripParens(x ast.Expr) ast.Expr {
	if px, strip := x.(*ast.ParenExpr); strip {
		// parentheses must not be stripped if there are any
		// unparenthesized composite literals starting with
		// a type name
		ast.Inspect(px.X, func(node ast.Node) bool {
			switch x := node.(type) {
			case *ast.ParenExpr:
				// parentheses protect enclosed composite literals
				return false
			case *ast.CompositeLit:
				if isTypeName(x.Type) {
					strip = false // do not strip parentheses
				}
				return false
			}
			// in all other cases, keep inspecting
			return true
		})
		if strip {
			return stripParens(px.X)
		}
	}
	return x
}
Exemple #2
0
// index all the spec definitions for inputs and outputs to element
func (p *compiler) indexParams(d []ast.Decl) {
	for i := range d {
		switch decl := d[i].(type) {
		case *ast.GenDecl:
			switch decl.Tok {
			case token.PARAMETERS, token.INPUTS:
				for j := range decl.Specs {
					tmp := decl.Specs[j].(*ast.ValueSpec)
					p.params = append(p.params, tmp)
					for k := range tmp.Names {
						//param := strings.Title(tmp.Names[k].String()) //TODO check input data cannot be lowercase
						param := tmp.Names[k].String()
						p.paramTypes[param] = p.getTypeString(tmp.Type)
						p.paramMap[param] = "p." + param
					}
				}
			case token.OUTPUTS, token.DATA:
				for j := range decl.Specs {
					tmp := decl.Specs[j].(*ast.ValueSpec)
					p.results = append(p.results, tmp)
					for k := range tmp.Names {
						if string(tmp.Names[k].String()[0]) == strings.ToUpper(string(tmp.Names[k].String()[0])) {
							result := tmp.Names[k].String()
							p.paramTypes[result] = p.getTypeString(tmp.Type)
							p.resultMap[result] = "r." + result //assign to resultblock, later staged
						} else { //lowercase, assign to resultblock
							result := tmp.Names[k].String()
							p.paramTypes[result] = p.getTypeString(tmp.Type)
							p.resultMap[result] = "r." + result
						}
					}
				}
			}
		case *ast.AnthaDecl:
			var ref ast.AnthaDecl
			ref = *decl
			tok := ref.Tok
			ast.Inspect(ref.Body, func(n ast.Node) bool {
				switch n.(type) {
				case *ast.Ident:
					if _, is := p.resultMap[n.(*ast.Ident).Name]; is {
						varName := n.(*ast.Ident).Name
						if _, ok := p.reuseMap[tok]; !ok {
							p.reuseMap[tok] = make(map[string]bool)
						}
						p.reuseMap[tok][varName] = true
					}
				}
				return true
			})
		default:
			continue
		}
	}
}
Exemple #3
0
func TestObjects(t *testing.T) {
	const src = `
package p
import fmt "fmt"
const pi = 3.14
type T struct{}
var x int
func f() { L: }
`

	f, err := ParseFile(fset, "", src, 0)
	if err != nil {
		t.Fatal(err)
	}

	objects := map[string]ast.ObjKind{
		"p":   ast.Bad, // not in a scope
		"fmt": ast.Bad, // not resolved yet
		"pi":  ast.Con,
		"T":   ast.Typ,
		"x":   ast.Var,
		"int": ast.Bad, // not resolved yet
		"f":   ast.Fun,
		"L":   ast.Lbl,
	}

	ast.Inspect(f, func(n ast.Node) bool {
		if ident, ok := n.(*ast.Ident); ok {
			obj := ident.Obj
			if obj == nil {
				if objects[ident.Name] != ast.Bad {
					t.Errorf("no object for %s", ident.Name)
				}
				return true
			}
			if obj.Name != ident.Name {
				t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
			}
			kind := objects[ident.Name]
			if obj.Kind != kind {
				t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
			}
		}
		return true
	})
}
Exemple #4
0
// Replace bare antha function names with go qualified names
func (p *compiler) sugarForIntrinsics(root ast.Node) {
	ast.Inspect(root, func(n ast.Node) bool {
		switch node := n.(type) {
		case nil:
			return false
		case *ast.Ident:
			// sugar the name if it is a known param
			if sugar, ok := p.intrinsicMap[node.Name]; ok {
				node.Name = sugar
			}
			// test if the left hand side of an assignment is to a results variable
			// if so, sugar it by replacing the = with a channel operation
		default:
		}
		return true
	})
}
Exemple #5
0
// Replace bare antha identifiers with go qualified names
func (p *compiler) sugarForParams(body *ast.BlockStmt) {
	list := body.List

	for i := range list {
		ast.Inspect(list[i], func(n ast.Node) bool {
			switch node := n.(type) {
			case nil:
				return false
			case *ast.Ident:
				// sugar the name if it is a known param
				if sugar, ok := p.paramMap[node.Name]; ok {
					node.Name = sugar
				} else if sugar, ok := p.resultMap[node.Name]; ok {
					node.Name = sugar
				}
				// test if the left hand side of an assignment is to a results variable
				// if so, sugar it by replacing the = with a channel operation
			case *ast.AssignStmt:
				// test if a result is being assigned to.
				fix_assign := false
				for j := range node.Lhs {
					if ident, ok := node.Lhs[j].(*ast.Ident); ok {
						isUpper := string(ident.Name[0]) == strings.ToUpper(string(ident.Name[0]))
						if sugar, fixme := p.resultMap[ident.Name]; fixme && isUpper {
							//fix_assign = true
							ident.Name = sugar
						}
					}
				}
				//TODO: generate multiple stmts if needed
				if fix_assign {
					if len(node.Rhs) > 1 {
						// currently undefined so panic
						panic("too many right hand exprs")
					}
					list[i] = &ast.SendStmt{node.Lhs[0], node.TokPos, p.wrapParamExpr(node.Rhs[0])}
				}
			default:

			}
			return true
		})
	}
}
Exemple #6
0
// This example demonstrates how to inspect the AST of a Go/Antha program.
func ExampleInspect() {
	// src is the input for which we want to inspect the AST.
	src := `
package p
const c = 1.0
var X = f(3.14)*2 + c
`

	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, "src.go", src, 0)
	if err != nil {
		panic(err)
	}

	// Inspect the AST and print all identifiers and literals.
	ast.Inspect(f, func(n ast.Node) bool {
		var s string
		switch x := n.(type) {
		case *ast.BasicLit:
			s = x.Value
		case *ast.Ident:
			s = x.Name
		}
		if s != "" {
			fmt.Printf("%s:\t%s\n", fset.Position(n.Pos()), s)
		}
		return true
	})

	// output:
	// src.go:2:9:	p
	// src.go:3:7:	c
	// src.go:3:11:	1.0
	// src.go:4:5:	X
	// src.go:4:9:	f
	// src.go:4:11:	3.14
	// src.go:4:17:	2
	// src.go:4:21:	c
}
Exemple #7
0
// playExample synthesizes a new *ast.File based on the provided
// file with the provided function body as the body of main.
func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
	if !strings.HasSuffix(file.Name.Name, "_test") {
		// We don't support examples that are part of the
		// greater package (yet).
		return nil
	}

	// Find top-level declarations in the file.
	topDecls := make(map[*ast.Object]bool)
	for _, decl := range file.Decls {
		switch d := decl.(type) {
		case *ast.FuncDecl:
			topDecls[d.Name.Obj] = true
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch s := spec.(type) {
				case *ast.TypeSpec:
					topDecls[s.Name.Obj] = true
				case *ast.ValueSpec:
					for _, id := range s.Names {
						topDecls[id.Obj] = true
					}
				}
			}
		}
	}

	// Find unresolved identifiers and uses of top-level declarations.
	unresolved := make(map[string]bool)
	usesTopDecl := false
	var inspectFunc func(ast.Node) bool
	inspectFunc = func(n ast.Node) bool {
		// For selector expressions, only inspect the left hand side.
		// (For an expression like fmt.Println, only add "fmt" to the
		// set of unresolved names, not "Println".)
		if e, ok := n.(*ast.SelectorExpr); ok {
			ast.Inspect(e.X, inspectFunc)
			return false
		}
		// For key value expressions, only inspect the value
		// as the key should be resolved by the type of the
		// composite literal.
		if e, ok := n.(*ast.KeyValueExpr); ok {
			ast.Inspect(e.Value, inspectFunc)
			return false
		}
		if id, ok := n.(*ast.Ident); ok {
			if id.Obj == nil {
				unresolved[id.Name] = true
			} else if topDecls[id.Obj] {
				usesTopDecl = true
			}
		}
		return true
	}
	ast.Inspect(body, inspectFunc)
	if usesTopDecl {
		// We don't support examples that are not self-contained (yet).
		return nil
	}

	// Remove predeclared identifiers from unresolved list.
	for n := range unresolved {
		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
			delete(unresolved, n)
		}
	}

	// Use unresolved identifiers to determine the imports used by this
	// example. The heuristic assumes package names match base import
	// paths for imports w/o renames (should be good enough most of the time).
	namedImports := make(map[string]string) // [name]path
	var blankImports []ast.Spec             // _ imports
	for _, s := range file.Imports {
		p, err := strconv.Unquote(s.Path.Value)
		if err != nil {
			continue
		}
		n := path.Base(p)
		if s.Name != nil {
			n = s.Name.Name
			switch n {
			case "_":
				blankImports = append(blankImports, s)
				continue
			case ".":
				// We can't resolve dot imports (yet).
				return nil
			}
		}
		if unresolved[n] {
			namedImports[n] = p
			delete(unresolved, n)
		}
	}

	// If there are other unresolved identifiers, give up because this
	// synthesized file is not going to build.
	if len(unresolved) > 0 {
		return nil
	}

	// Include documentation belonging to blank imports.
	var comments []*ast.CommentGroup
	for _, s := range blankImports {
		if c := s.(*ast.ImportSpec).Doc; c != nil {
			comments = append(comments, c)
		}
	}

	// Include comments that are inside the function body.
	for _, c := range file.Comments {
		if body.Pos() <= c.Pos() && c.End() <= body.End() {
			comments = append(comments, c)
		}
	}

	// Strip "Output:" comment and adjust body end position.
	body, comments = stripOutputComment(body, comments)

	// Synthesize import declaration.
	importDecl := &ast.GenDecl{
		Tok:    token.IMPORT,
		Lparen: 1, // Need non-zero Lparen and Rparen so that printer
		Rparen: 1, // treats this as a factored import.
	}
	for n, p := range namedImports {
		s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
		if path.Base(p) != n {
			s.Name = ast.NewIdent(n)
		}
		importDecl.Specs = append(importDecl.Specs, s)
	}
	importDecl.Specs = append(importDecl.Specs, blankImports...)

	// Synthesize main function.
	funcDecl := &ast.FuncDecl{
		Name: ast.NewIdent("main"),
		Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil
		Body: body,
	}

	// Synthesize file.
	return &ast.File{
		Name:     ast.NewIdent("main"),
		Decls:    []ast.Decl{importDecl, funcDecl},
		Comments: comments,
	}
}