Пример #1
0
// CheckStatement checks if a statement is type safe
func (scope *Scope) CheckStatement(node ast.Node) (errs []error) {
	for _, file := range scope.files {
		name := "." + scope.Render(file.Name) + ".gopry"
		if name == filepath.Base(scope.path) {
			ast.Walk(walker(func(n ast.Node) bool {
				switch s := n.(type) {
				case *ast.BlockStmt:
					for i, stmt := range s.List {
						pos := scope.fset.Position(stmt.Pos())
						if pos.Line == scope.line {
							r := scope.Render(stmt)
							if strings.HasPrefix(r, "pry.Apply") {
								var iStmt []ast.Stmt
								switch s2 := node.(type) {
								case *ast.BlockStmt:
									iStmt = append(iStmt, s2.List...)
								case ast.Stmt:
									iStmt = append(iStmt, s2)
								case ast.Expr:
									iStmt = append(iStmt, ast.Stmt(&ast.ExprStmt{X: s2}))
								default:
									errs = append(errs, errors.New("not a statement"))
									return false
								}
								oldList := make([]ast.Stmt, len(s.List))
								copy(oldList, s.List)

								s.List = append(s.List, make([]ast.Stmt, len(iStmt))...)

								copy(s.List[i+len(iStmt):], s.List[i:])
								copy(s.List[i:], iStmt)

								_, errs = scope.TypeCheck()
								if len(errs) > 0 {
									s.List = oldList
									return false
								}
								return false
							}
						}
					}
				}
				return true
			}), file)
		}
	}
	return
}
Пример #2
0
func (r *reader) block(b *block, s []ast.Stmt) {
	for _, s := range s {
		switch s := s.(type) {
		case *ast.AssignStmt:
			if s.Tok == token.DEFINE {
				switch x := s.Rhs[0].(type) {
				case *ast.BinaryExpr:
					n := newOperatorNode(types.NewFunc(0, nil, x.Op.String(), nil))
					b.addNode(n)
					r.in(x.X, n.ins[0])
					r.in(x.Y, n.ins[1])
					r.out(s.Lhs[0], n.outs[0])
				case *ast.CallExpr:
					if p, ok := x.Fun.(*ast.ParenExpr); ok { // writer puts conversions in parens for easy recognition
						n := newConvertNode(r.pkg)
						b.addNode(n)
						n.setType(r.typ(p.X))
						r.in(x.Args[0], n.ins[0])
						r.out(s.Lhs[0], n.outs[0])
					} else {
						n := r.call(b, x, "", s)
						for i, res := range s.Lhs {
							if i >= len(outs(n)) {
								out := n.(*callNode).newOutput(nil)
								out.bad = true
							}
							r.out(res, outs(n)[i])
						}
					}
				case *ast.CompositeLit:
					r.compositeLit(b, x, false, s)
				case *ast.FuncLit:
					n := newFuncNode(nil, b.childArranged)
					b.addNode(n)
					n.output.setType(r.typ(x.Type))
					r.out(s.Lhs[0], n.output)
					r.fun(n, x.Type, x.Body)
				case *ast.Ident, *ast.SelectorExpr, *ast.StarExpr:
					r.value(b, x, s.Lhs[0], false, s)
				case *ast.IndexExpr:
					r.index(b, x, s.Lhs[0], false, s)
				case *ast.SliceExpr:
					n := newSliceNode()
					b.addNode(n)
					r.in(x.X, n.x)
					r.in(x.Low, n.low)
					if x.High == nil {
						n.removePortBase(n.high)
						n.high = nil
					} else {
						r.in(x.High, n.high)
					}
					if x.Max != nil {
						n.max = n.newInput(newVar("max", types.Typ[types.Int]))
						r.in(x.Max, n.max)
					}
					r.out(s.Lhs[0], n.y)
				case *ast.TypeAssertExpr:
					n := newTypeAssertNode(r.pkg)
					b.addNode(n)
					n.setType(r.typ(x.Type))
					r.in(x.X, n.ins[0])
					r.out(s.Lhs[0], n.outs[0])
					r.out(s.Lhs[1], n.outs[1])
				case *ast.UnaryExpr:
					switch x.Op {
					case token.AND:
						switch y := x.X.(type) {
						case *ast.CompositeLit:
							r.compositeLit(b, y, true, s)
						case *ast.IndexExpr:
							r.index(b, y, s.Lhs[0], false, s)
						default:
							r.value(b, x, s.Lhs[0], false, s)
						}
					case token.NOT:
						n := newOperatorNode(types.NewFunc(0, nil, x.Op.String(), nil))
						b.addNode(n)
						r.in(x.X, n.ins[0])
						r.out(s.Lhs[0], n.outs[0])
					case token.ARROW:
						n := r.sendrecv(b, x.X, nil, s)
						r.out(s.Lhs[0], n.elem)
						r.out(s.Lhs[1], n.ok)
					}
				}
			} else {
				lh := s.Lhs[0]
				rh := s.Rhs[0]
				if x, ok := lh.(*ast.IndexExpr); ok {
					r.index(b, x, rh, true, s)
				} else if id, ok := lh.(*ast.Ident); !ok || r.conns[id.Name] == nil {
					r.value(b, lh, rh, true, s)
				} else {
					c := newConnection()
					lh := name(lh)
					rh := name(rh)
					c.setSrc(r.ports[rh])
					if cmt, ok := r.cmap[s]; ok {
						c.src.conntxt.SetText(cmt[0].List[0].Text[2:])
						c.toggleHidden()
					}
					if p, ok := r.ports[lh]; ok {
						c.feedback = true
						c.setDst(p)
					} else {
						r.conns[lh] = append(r.conns[lh], c)
					}
				}
			}
		case *ast.BranchStmt:
			n := newBranchNode(s.Tok.String())
			b.addNode(n)
			r.seq(n, s)
		case *ast.DeclStmt:
			decl := s.Decl.(*ast.GenDecl)
			v := decl.Specs[0].(*ast.ValueSpec)
			switch decl.Tok {
			case token.VAR:
				name := v.Names[0].Name
				if v.Type != nil {
					r.scope.Insert(newVar(name, r.typ(v.Type))) // local var has nil Pkg
					r.conns[name] = []*connection{}
				} else {
					r.out(v.Names[0], b.node.(*loopNode).inputsNode.outs[1])
				}
			case token.CONST:
				switch x := v.Values[0].(type) {
				case *ast.BasicLit:
					n := newBasicLiteralNode(x.Kind)
					b.addNode(n)
					switch x.Kind {
					case token.INT, token.FLOAT:
						n.text.SetText(x.Value)
					case token.IMAG:
						// TODO
					case token.STRING, token.CHAR:
						text, _ := strconv.Unquote(x.Value)
						n.text.SetText(text)
					}
					r.out(v.Names[0], n.outs[0])
					r.seq(n, s)
				case *ast.Ident, *ast.SelectorExpr:
					r.value(b, x, v.Names[0], false, s)
				}
			}
		case *ast.DeferStmt:
			r.call(b, s.Call, "defer ", s)
		case *ast.ExprStmt:
			switch x := s.X.(type) {
			case *ast.CallExpr:
				r.call(b, x, "", s)
			case *ast.UnaryExpr:
				r.sendrecv(b, x.X, nil, s)
			}
		case *ast.ForStmt:
			n := newLoopNode(b.childArranged)
			b.addNode(n)
			if s.Cond != nil {
				r.in(s.Cond.(*ast.BinaryExpr).Y, n.input)
			}
			if s.Init != nil {
				r.out(s.Init.(*ast.AssignStmt).Lhs[0], n.inputsNode.outs[0])
			}
			r.block(n.loopblk, s.Body.List)
			r.seq(n, s)
		case *ast.GoStmt:
			r.call(b, s.Call, "go ", s)
		case *ast.IfStmt:
			n := newIfNode(b.childArranged)
			b.addNode(n)
			for s := ast.Stmt(s); s != nil; {
				b, cond := n.newBlock()
				switch s2 := s.(type) {
				case *ast.IfStmt:
					r.in(s2.Cond, cond)
					r.block(b, s2.Body.List)
					s = s2.Else
				case *ast.BlockStmt:
					r.block(b, s2.List)
					s = nil
				}
			}
			r.seq(n, s)
		case *ast.RangeStmt:
			n := newLoopNode(b.childArranged)
			b.addNode(n)
			r.in(s.X, n.input)
			r.out(s.Key, n.inputsNode.outs[0])
			if s.Value != nil {
				r.out(s.Value, n.inputsNode.outs[1])
			}
			r.block(n.loopblk, s.Body.List)
			r.seq(n, s)
		case *ast.ReturnStmt:
			n := newBranchNode("return")
			b.addNode(n)
			r.seq(n, s)
		case *ast.SelectStmt:
			n := newSelectNode(b.childArranged)
			b.addNode(n)
			for _, s := range s.Body.List {
				s := s.(*ast.CommClause)
				c := n.newCase()
				switch s := s.Comm.(type) {
				case *ast.AssignStmt:
					c.send = false
					r.in(s.Rhs[0].(*ast.UnaryExpr).X, c.ch)
					r.out(s.Lhs[0], c.elemOk.outs[0])
					r.out(s.Lhs[1], c.elemOk.outs[1])
				case *ast.ExprStmt:
					c.send = false
					r.in(s.X.(*ast.UnaryExpr).X, c.ch)
				case *ast.SendStmt:
					r.in(s.Chan, c.ch)
					r.in(s.Value, c.elem)
				case nil:
					c.setDefault()
				}
				r.block(c.blk, s.Body)
			}
			r.seq(n, s)
		case *ast.SendStmt:
			r.sendrecv(b, s.Chan, s.Value, s)
		}
	}
}
Пример #3
0
// SuggestionsGoCode is a suggestion engine that uses gocode for autocomplete.
func (scope *Scope) SuggestionsGoCode(line string, index int) ([]string, error) {
	var suggestions []string
	var code string
	for name, file := range scope.Files {
		name = filepath.Dir(name) + "/." + filepath.Base(name) + "pry"
		if name == scope.path {
			ast.Walk(walker(func(n ast.Node) bool {
				switch s := n.(type) {
				case *ast.BlockStmt:
					for i, stmt := range s.List {
						pos := scope.fset.Position(stmt.Pos())
						if pos.Line == scope.line {
							r := scope.Render(stmt)
							if strings.HasPrefix(r, "pry.Apply") {
								var iStmt []ast.Stmt
								iStmt = append(iStmt, ast.Stmt(&ast.ExprStmt{X: ast.NewIdent(placeholder)}))
								oldList := make([]ast.Stmt, len(s.List))
								copy(oldList, s.List)

								s.List = append(s.List, make([]ast.Stmt, len(iStmt))...)

								copy(s.List[i+len(iStmt):], s.List[i:])
								copy(s.List[i:], iStmt)

								code = scope.Render(file)
								s.List = oldList
								return false
							}
						}
					}
				}
				return true
			}), file)

			i := strings.Index(code, placeholder) + index
			code = strings.Replace(code, placeholder, line, 1)

			subProcess := exec.Command("gocode", "autocomplete", filepath.Dir(name), strconv.Itoa(i))

			stdin, err := subProcess.StdinPipe()
			if err != nil {
				return nil, err
			}

			stdout, err := subProcess.StdoutPipe()
			if err != nil {
				return nil, err
			}
			defer stdout.Close()

			subProcess.Stderr = os.Stderr

			if err = subProcess.Start(); err != nil {
				return nil, err
			}

			io.WriteString(stdin, code)
			stdin.Close()

			output, err := ioutil.ReadAll(bufio.NewReader(stdout))
			if err != nil {
				return nil, err
			}
			rawSuggestions := strings.Split(string(output), "\n")[1:]
			for _, suggestion := range rawSuggestions {
				trimmed := strings.TrimSpace(suggestion)
				if len(trimmed) > 0 {
					suggestions = append(suggestions, trimmed)
				}
			}
			subProcess.Wait()

			break
		}
	}
	return suggestions, nil
}