let’s wr e an interpreter! - zenspider.com · 2020-05-16 · gogaruco 2013, san francisco, ca...

Post on 06-Jul-2020

1 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Let’s Wr!e an Interpreter!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Thank you so much!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Setting Expectations

• 168 Slides in 30 minutes. ~5.6 spm.

• Boatloads of content. Almost all code.

• I hope to “Hurt Brains”.

• TODO

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

How to solve a rubiks cube in 27

minutes

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Interpreters in 3 minutes

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

OMG WHY?!?

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

BecauseI’m a pervert!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Wa!… No!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Because we can!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

"e “Uby”Interpreter

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Branching Logic:if/else/elsif

if condition then true_codeelse false_codeend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Loops: whilewhile condition codeend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Local Variablesmeaning = 42pi = 3

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Functionsdef fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Functions

recursive function calls

local variables

“primitive” function callsdef fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Uby Roadmap

Variables Conditionals

Runtime

Environment

Functions

Parser

Loops

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Parsing: ruby_parser

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

ConditionalsLoops

Runtime

Environment

Functions

Parser

Variables

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

RubyParser.new.parse " def fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) end end"

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

:defn

:fib

:args:n

:if

:call

:lvar :n

:<

:lit 2

:lit 1

:call

:call

nil

:fib

:call

:lvar :n

:-

:lit 2:+

:call nil :fib

:call

:lvar :n

:-

:lit 1

Sunday, September 22, 13

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

:defn

:fib

:args:n

:if

:call

:lvar :n

:<

:lit 2

:lit 1

:call

:call

nil

:fib

:call

:lvar :n

:-

:lit 2:+

:call nil :fib

:call

:lvar :n

:-

:lit 1

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

:defn

:fib

:args

:n

:if

:call

:lvar :n

:<

:lit 2

:lit 1

:call

:call

nil

:fib

:call

:lvar :n

:-

:lit 2:+

:call nil :fib

:call

:lvar :n

:-

:lit 1

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

:defn

:fib

:args

:n

:if

:call

:lvar :n

:<

:lit 2

:lit 1

:call

:call

nil

:fib

:call

:lvar :n

:-

:lit 2:+

:call nil :fib

:call

:lvar :n

:-

:lit 1

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

else

:call nil :fib

:call

:lvar :n

:-

:lit 1

:call nil :fib

:call

:lvar :n

:-

:lit 2

:call :+

condition

:call

:lvar :n

:<

:lit 2

:defn

:fib :args

:n

:if

then

:lit 1

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

s(:defn, :fib, s(:args, :n), s(:if, s(:call, s(:lvar, :n), :<=, s(:lit, 2)), s(:lit, 1), s(:call, s(:call, nil, :fib, s(:call, s(:lvar, :n), :-, s(:lit, 2))), :+, s(:call, nil, :fib, s(:call, s(:lvar, :n), :-, s(:lit, 1))))))

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

S-Expression Vocabulary

s(:call, s(:lit, 3), :+, s(:lit, 4))

Sexp Sub-sexps

Rest(cdr)

Head/Type(car)

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

All for free using ruby_parser

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

OK…No more talk about

parsing.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Interpre#ng via sexp_processor

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

ConditionalsLoops

Environment

Variables

Functions

Parser

Runtime

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

How to get 7 from 3+4?

73 + 4 ??????

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Start with the source

3 + 4

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Parse it

3 + 4s(:call, s(:lit, 3), :+, s(:lit, 4))

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

s(:call, s(:lit, 3), :+, s(:lit, 4))

Then process by type

3 + 4

process_call

process_lit

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

s(:call, s(:lit, 3), :+, s(:lit, 4))

First with the inner values

3 + 4

process_lit

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

s(:call, 3, :+, s(:lit, 4))

First with the inner values

3 + 4

process_lit

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

s(:call, 3, :+, 4)

First with the inner values

3 + 4

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

s(:call, 3, :+, 4)

then with the outer

3 + 4

process_call

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

proc { |a, b| a + b }[3, 4]

then with the outer

3 + 4

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

proc { |a, b| a + b }[3, 4]

resulting in a final value

3 + 4 7

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

SexpProcessorclass UbyInterpreter def process_lit s s.last endend s(:lit, 3)

3

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Test Dr$en Interpreters

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Loops Conditionals

Runtime

Environment

Functions

Parser

Test

Dri

ven

Variables

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unit Testsdef test_sanity val = lang.eval("3 + 4") assert_equal 7, valend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unit TestsInput Source

Expected Value

def test_sanity val = lang.eval("3 + 4") assert_equal 7, valend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Premature Refactoring

Input SourceExpected Value

def assert_eval exp, src assert_equal exp, lang.eval(src)end

def test_sanity assert_eval 7, "3 + 4"end

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

San% Test:3 + 4

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

ConditionalsLoops

Environment

Functions

Parser

Test

Dri

ven

Runtime

Variables

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Test Infrastructureclass TestUbyInterpreter < MiniTest::Unit::TestCase attr_accessor :int

def setup self.int = UbyInterpreter.new end

def assert_eval exp, src, msg = nil assert_equal exp, int.eval(src), msg endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Our First Test:class TestUbyInterpreter < MiniTest::Unit::TestCase attr_accessor :int

def setup self.int = UbyInterpreter.new end

def assert_eval exp, src assert_equal exp, ri.eval(src) end

def test_sanity assert_eval 3, "3" assert_eval 7, "3 + 4" endend

Old Code

New Code

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Our First Test:class TestUbyInterpreter < MiniTest::Unit::TestCase

# ...

def test_sanity assert_eval 3, "3" assert_eval 7, "3 + 4" endend

Old Code

New Code

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Our first error:# Running tests:

E

Finished tests in 0.000364s, 2747.2527 tests/s, 0.0000 assertions/s.

1) Error:TestUbyInterpreter#test_sanity:NameError: uninitialized constant TestUbyInterpreter::UbyInterpreter ./test/test_ruby_interpreter.rb:11:in `setup'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define the constantclass UbyInterpreter < SexpInterpreter VERSION = "1.0.0"end

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Wash, rinse…# Running tests:

E

Finished tests in 0.000578s, 1730.1038 tests/s, 0.0000 assertions/s.

1) Error:TestUbyInterpreter#test_sanity:NoMethodError: private method `eval' called for #<UbyInterpreter:0x105be17d8> ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:19:in `test_sanity'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Implement eval & parseclass UbyInterpreter < SexpInterpreter # ... attr_accessor :parser

def initialize super

self.parser = Ruby19Parser.new end

def eval src process parse src end

def parse src self.parser.process src endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Finally!# Running tests:

E

Finished tests in 0.001365s, 732.6007 tests/s, 0.0000 assertions/s.

1) Error:TestUbyInterpreter#test_sanity:UnknownNodeError: Bug! Unknown node-type :lit to UbyInterpreter ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:19:in `test_sanity'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Implement process_litclass UbyInterpreter < SexpInterpreter

# ...

def process_lit s s.last endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Tada! Second Assertion! 1) Error:TestUbyInterpreter#test_sanity:UnknownNodeError: Bug! Unknown node-type :call to UbyInterpreter ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:20:in `test_sanity'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :call genericallyclass UbyInterpreter < SexpInterpreter

# ...

def process_call s raise "Boom: #{s.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

See sub-sexps 1) Error:TestUbyInterpreter#test_sanity:RuntimeError: Boom: s(:call, s(:lit, 3), :+, s(:lit, 4)) ./lib/ruby_interpreter.rb:35:in `process_call' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:17:in `assert_eval' ./test/test_ruby_interpreter.rb:22:in `test_sanity'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unpack sub-sexpsclass UbyInterpreter < SexpInterpreter

# ...

def process_call s _, recv, msg, *args = s

raise "Boom: #{recv}, #{msg}, #{args.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unpacked sub-sexps 1) Error:TestUbyInterpreter#test_sanity:RuntimeError: Boom: s(:lit, 3), +, [s(:lit, 4)] ./lib/ruby_interpreter.rb:31:in `process_call'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Process sub-sexpsclass UbyInterpreter < SexpInterpreter def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

raise "Boom: #{recv}, #{msg}, #{args.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Processed Values 1) Error:TestUbyInterpreter#test_sanity:RuntimeError: Boom: 3, +, [4] ./lib/ruby_interpreter.rb:43:in `process_call'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Cheat: Send to Rubyclass UbyInterpreter < SexpInterpreter def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

recv.send(msg, *args) # big ol' hack… endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sanity!# Running tests:

.

Finished tests in 0.001625s, 615.3846 tests/s, 1230.7692 assertions/s.

1 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!class UbyInterpreter < SexpInterpreter VERSION = "1.0.0"

attr_accessor :parser

def initialize super self.parser = Ruby19Parser.new end

def eval src process parse src end

def parse src self.parser.process src end

def process_lit s s.last end

def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

recv.send(msg, *args) # big ol' hack… endend

Yay!

Overblown

Calculator!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Cond!ionals&

Truthiness

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Loops

Runtime

Environment

Variables

Functions

Parser

Test

Dri

ven

Conditionals

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Add :if testsclass TestUbyInterpreter < MiniTest::Unit::TestCase # ...

def test_if assert_eval 42, "if true then 42 else 24 end" end

def test_if_falsey assert_eval 24, "if nil then 42 else 24 end" assert_eval 24, "if false then 42 else 24 end" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unknown :if 1) Error:TestUbyInterpreter#test_if:UnknownNodeError: Bug! Unknown node-type :if to UbyInterpreter ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:34:in `test_if'

2) Error:TestUbyInterpreter#test_if_falsey:UnknownNodeError: Bug! Unknown node-type :if to UbyInterpreter ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:38:in `test_if_falsey'

3 tests, 2 assertions, 0 failures, 2 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :if genericallyclass UbyInterpreter < SexpInterpreter # ...

def process_if s raise "Boom: #{s.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

See sub-sexps 1) Error:TestUbyInterpreter#test_if:RuntimeError: Boom: s(:if, s(:true), s(:lit, 42), s(:lit, 24)) ./lib/ruby_interpreter.rb:38:in `process_if' ... ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:34:in `test_if'

2) Error:TestUbyInterpreter#test_if_falsey:RuntimeError: Boom: s(:if, s(:nil), s(:lit, 42), s(:lit, 24)) ./lib/ruby_interpreter.rb:38:in `process_if' ... ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:38:in `test_if_falsey'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Process sub-sexpsclass UbyInterpreter < SexpInterpreter # ...

def process_if s _, c, t, f = s

c = process c

if c then process t else process f end endend

Why evaluate c before t &

f?

if true then puts "happy"else system "rm -rf /"end

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sub-sexps fail 1) Error:TestUbyInterpreter#test_if:UnknownNodeError: Bug! Unknown node-type :true to UbyInterpreter ./lib/ruby_interpreter.rb:40:in `process_if' ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:34:in `test_if'

2) Error:TestUbyInterpreter#test_if_falsey:UnknownNodeError: Bug! Unknown node-type :nil to UbyInterpreter ./lib/ruby_interpreter.rb:40:in `process_if' ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:38:in `test_if_falsey'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :nil and :trueclass UbyInterpreter < SexpInterpreter # ...

def process_nil s nil end

def process_true s true endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Third failure 1) Error:TestUbyInterpreter#test_if_falsey:UnknownNodeError: Bug! Unknown node-type :false to UbyInterpreter ./lib/ruby_interpreter.rb:40:in `process_if' ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:39:in `test_if_falsey'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :falseclass UbyInterpreter < SexpInterpreter # ...

def process_false s false endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Voilà!# Running tests:

...

Finished tests in 0.002860s, 1048.9510 tests/s, 1748.2517 assertions/s.

3 tests, 5 assertions, 0 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Test Driven Process

Processsub-sexps

Tests Pass

Add afailing test

Errornode-type

Add genericnode-type

Failure sub-sexpcomponents

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Yes, development this fast…

with autotest.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Brain Hurt Yet?

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Good

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Wait till I get going!

Where was I?

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Local Variables

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Loops Conditionals

Runtime

Functions

Parser

Test

Dri

ven

Variables

Environment

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Local Variables!class TestUbyInterpreter < MiniTest::Unit::TestCase # ...

def test_lvar assert_eval 42, "x = 42; x" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Block?!? 1) Error:TestUbyInterpreter#test_lvar:UnknownNodeError: Bug! Unknown node-type :block to UbyInterpreter ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:24:in `test_lvar'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :blockclass UbyInterpreter < SexpInterpreter # ...

def process_block s raise "Boom: #{s.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Ohhh… 1) Error:TestUbyInterpreter#test_lvar:RuntimeError: Boom: s(:block, s(:lasgn, :x, s(:lit, 42)), s(:lvar, :x)) ./lib/ruby_interpreter.rb:25:in `process_block' ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:24:in `test_lvar'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Process all sub-sexpsclass UbyInterpreter < SexpInterpreter # ...

def process_block s result = nil s.rest.each do |sub| result = process sub end result endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

:lasgn is undefined 1) Error:TestUbyInterpreter#test_lvar:UnknownNodeError: Bug! Unknown node-type :lasgn to UbyInterpreter ./lib/ruby_interpreter.rb:27:in `process_block' ./lib/ruby_interpreter.rb:26:in `each' ./lib/ruby_interpreter.rb:26:in `process_block' ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:24:in `test_lvar'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :lasgnclass UbyInterpreter < SexpInterpreter # ...

def process_lasgn s raise "Boom: #{s.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Looks like name/value 1) Error:TestUbyInterpreter#test_lvar:RuntimeError: Boom: s(:lasgn, :x, s(:lit, 42)) ./lib/ruby_interpreter.rb:58:in `process_lasgn' ./lib/ruby_interpreter.rb:27:in `process_block' ./lib/ruby_interpreter.rb:26:in `each' ./lib/ruby_interpreter.rb:26:in `process_block' ./lib/ruby_interpreter.rb:17:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:24:in `test_lvar'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Add a stupid tableclass UbyInterpreter < SexpInterpreter # ...

attr_accessor :env

def initialize # ... self.env = {} # omg this is horrible end

def process_lasgn s _, n, v = s

self.env[n] = process v endend

EnvironmentEnvironment

x 42

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Now we need to read 1) Error:TestUbyInterpreter#test_lvar:UnknownNodeError: Bug! Unknown node-type :lvar to UbyInterpreter ./lib/ruby_interpreter.rb:29:in `process_block' ./lib/ruby_interpreter.rb:28:in `each' ./lib/ruby_interpreter.rb:28:in `process_block' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:24:in `test_lvar'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Access the tableclass UbyInterpreter < SexpInterpreter # ...

def process_lvar s _, name = s

self.env[name] endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Voilà# Running tests:

....

Finished tests in 0.003666s, 1091.1075 tests/s, 1636.6612 assertions/s.

4 tests, 6 assertions, 0 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Just wait…

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

It gets “better”

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Fun&ions

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Conditionals

Runtime

Environment

Variables

Parser

Test

Dri

ven

Loops

Functions

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Add :defn Testclass TestUbyInterpreter < MiniTest::Unit::TestCase # ...

def test_defn assert_eval nil, <<-EOM def double n 2 * n end EOM

assert_eval 42, "double(21)" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unknown :defn 1) Error:TestUbyInterpreter#test_defn:UnknownNodeError: Bug! Unknown node-type :defn to UbyInterpreter ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:17:in `assert_eval' ./test/test_ruby_interpreter.rb:45:in `test_defn'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Generic :defnclass UbyInterpreter < SexpInterpreter # ...

def process_defn s raise "Boom: #{s.inspect}" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Examine sub-sexps 1) Error:TestUbyInterpreter#test_defn:RuntimeError: Boom: s(:defn, :double, s(:args, :n), s(:call, s(:lit, 2), :*, s(:lvar, :n))) ./lib/ruby_interpreter.rb:44:in `process_defn' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:17:in `assert_eval' ./test/test_ruby_interpreter.rb:45:in `test_defn'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unpack & Storeclass UbyInterpreter < SexpInterpreter # ...

def process_defn s _, name, args, *body = s

self.env[name] = [args, body]

nil endend

EnvironmentEnvironment

n 8

life 42

pi 3

double

[ s(:args, :n),

[s(:call, s(:lit, 2), :*, s(:lvar, :n))]]

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

What’s up with :call? 1) Error:TestUbyInterpreter#test_defn:NoMethodError: undefined method `double' for nil:NilClass ./lib/ruby_interpreter.rb:40:in `send' ./lib/ruby_interpreter.rb:40:in `process_call' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:17:in `assert_eval' ./test/test_ruby_interpreter.rb:54:in `test_defn'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

No NilClass#doubleclass UbyInterpreter < SexpInterpreter # ...

def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

recv.send(msg, *args) # big ol' hack… endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Isolate the Problemclass UbyInterpreter < SexpInterpreter # ...

def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

if recv then recv.send(msg, *args) # less of a hack else raise "argh" end endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Error, Isolated 1) Error:TestUbyInterpreter#test_defn:RuntimeError: argh ./lib/ruby_interpreter.rb:43:in `process_call' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:17:in `assert_eval' ./test/test_ruby_interpreter.rb:54:in `test_defn'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Set vars & run the codeclass UbyInterpreter < SexpInterpreter # ...

def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

if recv then recv.send(msg, *args) # less of a hack else decls, body = self.env[msg]

decls.rest.zip(args).each do |name, val| self.env[name] = val end

process_block s(:block, *body) end endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

args... decls... zip bwuh?

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

EnvironmentEnvironment

n 21

life 42

pi 3

double

[ s(:args, :n),

s(:call, s(:lit, 2), :*, s(:lvar, :n))]

EnvironmentEnvironment

n 8

life 42

pi 3

double

[ s(:args, :n),

s(:call, s(:lit, 2), :*, s(:lvar, :n))]

s(:call, nil, :double, s(:lit, 21))

decls.rest.zip(args).each do |k, v| self.env[k] = vend

[:n].zip([21]).each do |k, v| self.env[k] = vend

self.env[:n] = 21

decls

args

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Voilà!# Running tests:

.....

Finished tests in 0.011826s, 422.7972 tests/s, 676.4756 assertions/s.

5 tests, 8 assertions, 0 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

“Getting to green just means you don’t have enough

tests.”– Kent Beck

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Fibonacci

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Conditionals

Runtime

Parser

Test

Dri

ven

Environment

Loops

Functions

Variables

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Finally Freakin’ Fibonacci!class TestUbyInterpreter < MiniTest::Unit::TestCase # ...

def test_fib assert_eval nil, <<-END def fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) end end END

assert_eval 8, "fib(6)" endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Whoa… it ran… but failed 1) Failure:TestUbyInterpreter#test_fib [./test/test_ruby_interpreter.rb:71]:Expected: 8 Actual: 3

6 tests, 10 assertions, 1 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

EnvironmentEnvironment

n 4

life 42

pi 3

fib

[ s(:args, :n),

s(:if, ...)]

EnvironmentEnvironment

n 6

life 42

pi 3

fib

[ s(:args, :n),

s(:if, ...)]

EnvironmentEnvironment

n 8

life 42

pi 3

fib

[ s(:args, :n),

s(:if, ...)]

That stupid Table?Yeah...

def fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) endendn = 8; fib(6)n = 6; fib(4) + fib(5)n = 4; ... and so on...

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

[ s(:args, :n),

s(:if, ...)]

fib

3pi

42life

n 8

[ s(:args, :n),

s(:if, ...)]

fib

3pi

42life

n 5

[ s(:args, :n),

s(:if, ...)]

fib

3pi

42life

n 3

[ s(:args, :n),

s(:if, ...)]

fib

3pi

42life

n 1

Wanted: Stacked Hashn 1

n 3

n 5

[ s(:args, :n),

s(:if, ...)]

fib

3pi

42life

n 8

fib(1)

fib(3)

fib(5)

original

but acts like:

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

6z

5y

4x

2b

3c

42x

1a

z 6

y 5

x 42

3c

2b

1a

Environment Classenv[:x] == ?

env[:x] = 42

all:

scope:

scope:

scope:

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!class UbyInterpreter < SexpInterpreter # ...

class Environment def [] k self.all[k] end

def []= k, v @env.last[k] = v end

def all @env.inject(&:merge) end

def scope @env.push({})

yield ensure @env.pop end

def initialize @env = [{}] end endend

Writes to top layer only

Reads from everything

Flattened. Newest wins

Automatic layering

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Replace stupid tableclass UbyInterpreter < SexpInterpreter # ...

def initialize # ... self.env = Environment.new end

# ...end

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Scope every callclass UbyInterpreter < SexpInterpreter # ...

def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

if recv then recv.send(msg, *args) # less of a hack else self.env.scope do decls.rest.zip(args).each do |name, val| self.env[name] = val end

process_block s(:block, *body) end end endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Finally# Running tests:

......

Finished tests in 0.016849s, 356.1042 tests/s, 712.2084 assertions/s.

6 tests, 12 assertions, 0 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

While Loops

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Runtime

Environment

Variables

Functions

Parser

Test

Dri

ven

ConditionalsLoops

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Refactor Fibonnaciclass TestUbyInterpreter < MiniTest::Unit::TestCase # ...

def define_fib assert_eval nil, <<-END def fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) end end END endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Add :while testsclass TestUbyInterpreter < MiniTest::Unit::TestCase # ...

def test_while_sum_of_fibs define_fib

assert_eval 1+1+2+3+5+8+13+21+34+55, <<-EOM n = 1 sum = 0 while n <= 10 sum += fib(n) n += 1 end sum EOM endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Unknown :while 1) Error:TestUbyInterpreter#test_while_fib:UnknownNodeError: Bug! Unknown node-type :while to UbyInterpreter ./lib/ruby_interpreter.rb:29:in `process_block' ./lib/ruby_interpreter.rb:28:in `each' ./lib/ruby_interpreter.rb:28:in `process_block' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:86:in `test_while_fib'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :while genericallyclass UbyInterpreter < SexpInterpreter # ...

def process_while s raise "Boom: #{s.inspect} endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

See sub-sexps 1) Error:TestUbyInterpreter#test_while:RuntimeError: Boom: s(:while, s(:call, s(:lvar, :n), :<, s(:lit, 41)), s(:lasgn, :n, s(:call, s(:lvar, :n), :+, s(:lit, 1))), true) ./lib/ruby_interpreter.rb:104:in `process_while' ./lib/ruby_interpreter.rb:29:in `process_block' ./lib/ruby_interpreter.rb:28:in `each' ./lib/ruby_interpreter.rb:28:in `process_block' ./lib/ruby_interpreter.rb:19:in `eval' ./test/test_ruby_interpreter.rb:15:in `assert_eval' ./test/test_ruby_interpreter.rb:62:in `test_while'

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Define :whileclass UbyInterpreter < SexpInterpreter # ...

def process_while s _, cond, *body = s body.pop # pre vs post condition -- ignore for now

while process cond process_block s(:block, *body) end endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Voilà# Running tests:

.......

Finished tests in 0.020218s, 346.2261 tests/s, 544.0696 assertions/s.

7 tests, 11 assertions, 0 failures, 0 errors, 0 skips

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

What Have We Done?

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!require "ruby_parser"require "sexp_processor"

class UbyInterpreter < SexpInterpreter VERSION = "1.0.0"

attr_accessor :parser attr_accessor :env

def initialize super

self.parser = Ruby19Parser. new self.env = Environment.new end

def eval src process parse src end

def parse src self.parser.process src end

def process_block s result = nil s.rest.each do |sub| result = process sub end result end

def process_call s _, recv, msg, *args = s

recv = process recv args.map! { |sub| process sub }

if recv then recv.send(msg, *args) else d, body = self.env[msg]

self.env.scope do d.rest.zip(args). each do |k, v| self.env[k] = v end

process_block s(:block, *body) end end end

def process_defn s _, name, args, *body = s

self.env[name] = [args, body]

nil end

def process_false s false end

def process_if s _, c, t, f = s

c = process c

if c then process t else process f end end

def process_lasgn s _, name, val = s

self.env[name] = process val end

def process_lit s s.last end

def process_lvar s _, name = s

self.env[name] end

def process_nil s nil end

def process_true s true end

def process_while s _, cond, *body = s body.pop

while process cond process_block s(:block, *body) end end

class Environment def [] k self.all[k] end

def []= k, v @env.last[k] = v end

def all @env.inject(&:merge) end

def scope @env.push({})

yield ensure @env.pop end

def initialize @env = [{}] end endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

class TestUbyInterpreter < MiniTest::Unit::TestCase attr_accessor :int

def setup self.int = UbyInterpreter.new end

def assert_eval exp, src, msg = nil assert_equal exp, int.eval(src), msg end

def define_fib assert_eval nil, <<-END def fib n if n <= 2 then 1 else fib(n-2) + fib(n-1) end end END end

def test_sanity assert_eval 3, "3" assert_eval 7, "3 + 4" end

def test_defn assert_eval nil, <<-EOM n = 24 def double n 2 * n end EOM

assert_eval 42, "double(21)" end

def test_fib define_fib

assert_eval 8, "fib(6)" end

def test_if assert_eval 42, "if true then 42 else 24 end" end

def test_if_falsey assert_eval 24, "if nil then 42 else 24 end" assert_eval 24, "if false then 42 else 24 end" end

def test_lvar assert_eval 42, "x = 42; x" end

def test_while_fib define_fib

assert_eval 1+1+2+3+5+8+13+21+34+55, <<-EOM n = 1 sum = 0 while n <= 10 sum += fib(n) n += 1 end sum EOM endend

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

“Uby” Language

• Basic numeric types, true, false, nil.• Conditional branching and looping.• Primitive & user defined functions.• Local variables & variable scoping.• Test-driven, extensible, patterns-based design.• ~2 hours, ~130 LOC impl, ~70 LOC test.• Fits in one head.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

What Else Can We Do?• Add richer types: strings, arrays, hashes, etc.

• Enforce different scoping rules (eg. ruby's opaque def vs scheme's transparent lambdas).

• Implement recursive tail-calls or closures.

• Add an object system.

• Change the way functions are called.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Further Study

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Fantastic Books

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

No time for questions.

Please grab me at the afterparty.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

"ank You…

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

This is the last talk!

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

I get to correct an injustice…

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

…from twitter.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

He who made it…

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

…defines it.

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

“Sea!le S"le”

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Warning Free

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

No extra syntax

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Not in 'def' lines

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Not in calls

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Nowhere

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

This:remove_parens while warning_free and passes_tests

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Not this:remove_parens() while (warning_free() and passes_tests())

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Not Seattle Styledef method() call()end

def method(arg) call(arg)end

def method(arg1, arg2) call(arg1, arg2)end

call1(call2(call3()))

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

Seattle Styledef method callend

def method arg call argend

def method arg1, arg2 call arg1, arg2end

call1 call2 call3

call(/regexp/) # ambiguous w/o parens

Sunday, September 22, 13

GoGaRuCo 2013, San Francisco, CA

Ryan Davis, Seattle.rbLet’s Write an Interpreter!

"ank You!

Sunday, September 22, 13

top related