gore: go repl

51
A Tale of Go REPL motemen Go Conference 2015 Summer @ Tokyo

Upload: hiroshi-shibamura

Post on 06-Aug-2015

75 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Gore: Go REPL

A Tale of Go REPLmotemen Go Conference 2015 Summer @ Tokyo

Page 2: Gore: Go REPL

About Me

- @motemen

- Web engineer

- Chief engineer at Hatena, Kyoto

Page 3: Gore: Go REPL

Mackerel mackerel.io

Page 4: Gore: Go REPL

Agenda

- Introduction of ! motemen/gore

- Gore’s architecture

- Implementation details

Page 5: Gore: Go REPL

Gore /gɔː/

Page 6: Gore: Go REPL

Case: Learning New Languages

- Playing with language features

- Trying libraries

Page 7: Gore: Go REPL

My Case — Perl

- perl ~/sketch.pl

- Edit and Run

Page 8: Gore: Go REPL

My Case — Ruby- irb

- pry

- A “REPL”

(Read-Eval-Print-Loop)

Page 9: Gore: Go REPL

My Case — Go?- tmp.go

- “packge main” “func main()”

- Removing unused variables by hand

- Question: Can gophers haz a REPL?

Page 10: Gore: Go REPL

Go "REPL" impls (AFAIK)- ! sbinet/igo — built upon ! sbinet/go-eval, with liner

- ! emicklei/rango — importing, statements, listing variables

- ! vito/go-repl — importing, statements, useful commands

- ! d4l3k/go-pry — attaches to running program, uses reflection

- (play.golang.org)

Page 11: Gore: Go REPL

Needed a REPL that:- Evaluates statements

- Imports any module

- Pretty prints values automatically

- Completes code

Page 12: Gore: Go REPL

The Difficulties

- How can we achieve Go semantics?

- No APIs provided

- Re-implement Go compiler? 😩

Page 13: Gore: Go REPL

Solution: “go run”

Page 14: Gore: Go REPL

Solution: “go run”- An “eval” to Go

- Perfect implementation & Always up-to-date

- Dead simple

- In: a program Out: the result 🎉

Page 15: Gore: Go REPL

Gore: A REPL using “go run”

go get github.com/motemen/gore

Page 16: Gore: Go REPL

Caveats 🙏

- Gore runs all code input for each run

- Code with side effects will be repeated

- eg. Printing output / Sleeping

Page 17: Gore: Go REPL

Stories Inside Gore

Page 18: Gore: Go REPL

Overview: not a “REPL” exactly- REPL: Read-Eval-Print-Loop

- No “Eval” and “Print” phase

- As we do them by “go run”

- Much like: Read-Generate-Run-Loop

Page 19: Gore: Go REPL

Read-Gen-Run-Loop1. Read user input to evaluate

2. Generate a .go file that prints result

3. “go run” it

4. Back to 1.

Page 20: Gore: Go REPL

Step: Read- Codes go under AST (abstract syntax tree) form

- Syntax checking

- Easier to manipulate

- Input String → AST → .go → “go run”

Page 21: Gore: Go REPL

Read: Input- ! peterh/liner

- Line editing

- History

- Supports Windows

Page 22: Gore: Go REPL

Read: Input to AST

- package go/parser

- Go has rich support for parsing/typing its code

- Easy to generate & manipulate

Page 23: Gore: Go REPL

Read: Input to AST

- Parse input as an expression

- If failed: a statement

- Otherwise: continue input (multi-line)

Page 24: Gore: Go REPL

Read: Expression

- Easy

- Dedicated API

- parser.ParseExpr(in)

Page 25: Gore: Go REPL

Read: Statements - $

func (s *Session) evalStmt(in string) error { src := fmt.Sprintf("package P; func F() { %s }", in) f, err := parser.ParseFile(s.Fset, "stmt.go", src, 0) if err != nil { return err }

enclosingFunc := f.Scope.Lookup("F").Decl.(*ast.FuncDecl) stmts := enclosingFunc.Body.List }

Page 26: Gore: Go REPL

Generate: The Delicious Part

- Append the input AST nodes to our main()

- Add the code to print the value

Page 27: Gore: Go REPL

Printing Values- The generated program prints values (not gore)

- “New Values” should be printed

- Expression: Its resulting value

- Statement: Values assigned

Page 28: Gore: Go REPL

Printing Values: Expression

- User: foo.Bar(a+1)

- Gore: __gore_p(foo.Bar(a+1))

Page 29: Gore: Go REPL

Printing Values: Statements

- ast.AssignStmt stands for assign/define

- User: a, b := foo()

- Gore: a, b := foo(); __gore_p(a, b)

Page 30: Gore: Go REPL

Printing Values: __gore_p()- “Pretty prints” values

- Defined along with the main()

- Depends on installed packages

- ! k0kubun/pp, ! davecgh/go-spew or plain old %#v

Page 31: Gore: Go REPL

The Initial .go File (pp)package main

import "github.com/k0kubun/pp"

func __gore_p(xx ...interface{}) { for _, x := range xx { pp.Println(x) } } func main() { // User input goes here }

Page 32: Gore: Go REPL

The Initial .go File (go-spew)package main

import "github.com/davecgh/go-spew/spew"

func __gore_p(xx ...interface{}) { for _, x := range xx { spew.Printf("%#v\n", x) } } func main() { // User input goes here }

Page 33: Gore: Go REPL

The Initial .go File (%#v)package main

import "fmt"

func __gore_p(xx ...interface{}) { for _, x := range xx { fmt.Printf("%#v\n", x) } } func main() { // User input goes here }

Page 34: Gore: Go REPL

Generate: Append to main()

- Just append those generated statements

s.mainBody.List = append(s.mainBody.List, stmts…)

- Now we’ve got the working code!

- Really? No! %

Page 35: Gore: Go REPL

Go compiler complaints (you know)

- “x declared but not used”

- “p imported but not used”

- “no new variables of left side of :=“

- & Should remove these to a successful go run

Page 36: Gore: Go REPL

Quick Fixing Erroneous Program

- “x declared but not used”

- “p imported but not used”

- “no new variables on left side of :=“

Use it!!

Anonymize it!!

Make it `=‘ !!

Page 37: Gore: Go REPL

“declared but not used”

package P

func main() { s := "hello" }

package P

func main() { s := "hello" _ = s }

Page 38: Gore: Go REPL

”imported but not used”package P

import "fmt"

func main() { }

package P

import _ "fmt"

func main() { }

Page 39: Gore: Go REPL

“no new variables on left side of :=“

package P

func main() { var a int a := 1 }

package P

func main() { var a int a = 1 }

Page 40: Gore: Go REPL

! motemen/go-quickfix to do the work

- Type check source code using go/types.Check()

- Catch errors and modify AST

- Packed with a bin

- goquickfix -w main.go

Page 41: Gore: Go REPL

Gore specific quickfix

- __gore_p() for non-value expressions

Page 42: Gore: Go REPL

Running

- Output the AST to file — go/printer

- Then go run

- If failed to run, revert the last input

Page 43: Gore: Go REPL

Recap: Read-Gen-Quickfix-Run-Loop1. Read and parse the input to obtain AST

2. Add printing function call __gore_p()

3. Append the code to main()

4. Quickfix it so that it compiles well

5. go run

Page 44: Gore: Go REPL

PRs welcome! 🍻

- ! motemen/gore

- ! motemen/go-quickfix

Page 45: Gore: Go REPL

htn.to/intern2015

Page 46: Gore: Go REPL

A Tale Goremotemen

Go Conference 2015 Summer @ Tokyo

Page 47: Gore: Go REPL

Appendix:

More Features

Page 48: Gore: Go REPL

Code Completion- liner has support for completion

- Great tool for editors: ! nsf/gocode

- Server/client model

- And an interface to it: motemen/gore/gocode

- If gocode binary can be located, use it

Page 49: Gore: Go REPL

Commands- :print

- :write <file>

- :import <pkg>

- :doc <expr>

Page 50: Gore: Go REPL

:import <pkg>

- Imports arbitrary packages

- Just append import statement to the file

- Unused imports are handled by quickfix

Page 51: Gore: Go REPL

:doc <expr>- Invokes go doc for given expression

- Package: :doc json

- Function: :doc json.Marshal

- Method: :doc json.NewEncoder().Encode