Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Slaying the Dragon

Slaying the Dragon

A talk that I gave at RedDotRubyConf 2016

Jason Yeo

June 23, 2016
Tweet

Other Decks in Programming

Transcript

  1. MAL

  2. mal> (+ 2 6) => 8 mal> (- 4 8)

    => -4 mal> (+ (- 4 8) 10)
  3. mal> (+ 2 6) => 8 mal> (- 4 8)

    => -4 mal> (+ (- 4 8) 10) => 6 mal>
  4. mal> (+ 2 6) => 8 mal> (- 4 8)

    => -4 mal> (+ (- 4 8) 10) => 6 mal> (< 4 10)
  5. mal> (+ 2 6) => 8 mal> (- 4 8)

    => -4 mal> (+ (- 4 8) 10) => 6 mal> (< 4 10) true
  6. mal> (fn* [x] (* x x)) => #<function> mal> ((fn*

    [x] (* x x)) 8) FUNCTION THAT YOU WANT TO CALL
  7. mal> (fn* [x] (* x x)) => #<function> mal> ((fn*

    [x] (* x x)) 8) => 64 mal> (def sq (fn* [x] (* x x)))
  8. mal> (fn* [x] (* x x)) => #<function> mal> ((fn*

    [x] (* x x)) 8) => 64 mal> (def sq (fn* [x] (* x x))) => #<function>
  9. mal> (fn* [x] (* x x)) => #<function> mal> ((fn*

    [x] (* x x)) 8) => 64 mal> (def sq (fn* [x] (* x x))) => #<function> mal> (sq 8)
  10. mal> (fn* [x] (* x x)) => #<function> mal> ((fn*

    [x] (* x x)) 8) => 64 mal> (def sq (fn* [x] (* x x))) => #<function> mal> (sq 8) => 64
  11. [1] pry(main)> require 'ripper' => true [2] pry(main)> Ripper.lex 'foo

    = 123' => [[[1, 0], :on_ident, "foo"], [[1, 3], :on_sp, " "], [[1, 4], :on_op, "="], [[1, 5], :on_sp, " "], [[1, 6], :on_int, "123"]]
  12. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  13. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false 'IS A' OR 'CAN BE EXPANDED TO'
  14. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false OR
  15. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false ONE OR MORE
  16. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false ZERO OR MORE
  17. <sentence> ::= <subject> <verb> <object> <subject> ::= <noun> <object> ::=

    <noun> <verb> ::= eat | code | love <noun> ::= I | He | She | Ruby | Python
  18. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  19. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  20. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  21. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  22. <form> ::= <list> | <atom> def read_form(tokens) if tokens.first =~

    /(\(|\[)/ read_list(tokens) else read_atom(tokens.shift) end end
  23. <form> ::= <list> | <atom> <list> ::= '(' <form>+ ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  24. <list> ::= '(' <form>+ ')' | '[' <form>* ']' def

    read_list(tokens) list = [:list] tokens.shift # pop our opening paren while tokens.first !~ /(\)|\])/ list << read_form(tokens) end tokens.shift # pop our closing paren list end
  25. <form> ::= <list> | <atom> <list> ::= '(' <form>* ')'

    | '[' <form>* ']' <atom> ::= a-z+ | 0-9+ | true | false
  26. <atom> ::= a-z+ | 0-9+ | true | false def

    read_atom(token) case token when /^-?\d+$/ [:integer, token.to_i] when 'true' [:boolean, :true] when 'false' [:boolean, :false] when /^\D+$/ [:symbol, token] else raise 'Reader error: Unknown token' end end
  27. ['(', '+', 'my_var', '42', ')'] (+ my_var 42) [:list, [:symbol,

    '+'], [:symbol, 'my_var'], [:integer, 42]]
  28. class AddNode < Node attr_reader :left_operand, :right_operand def initialize(left_operand, right_operand)

    super @left_operand = left_operand @right_operand = right_operand end end BOTH ARE NODE OBJECTS
  29. STACK 1 + 2 push 1 push 2 add INSTRUCTIONS

    1 2 POPS OPERANDS AND PUSHES RESULT ON THE STACK
  30. STACK (1 + 2) * 3 push 1 push 2

    add INSTRUCTIONS 3
  31. STACK (1 + 2) * 3 push 1 push 2

    add push 3 INSTRUCTIONS 3 3
  32. STACK (1 + 2) * 3 push 1 push 2

    add push 3 multiply INSTRUCTIONS 3 3
  33. STACK (1 + 2) * 3 push 1 push 2

    add push 3 multiply INSTRUCTIONS 9
  34. [1] pry(main)> code = '(15 + 6) * 2' =>

    '(15 + 6) * 2' [2] pry(main)>
  35. [1] pry(main)> code = '(15 + 6) * 2' =>

    '(15 + 6) * 2' [2] pry(main)> puts RubyVM::InstructionSequence.compile(code).disasm == disasm: #<ISeq:<compiled>@<compiled>>================================ 0000 trace 1 ( 1) 0002 putobject 15 0004 putobject 6 0006 opt_plus <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache> 0009 putobject 2 0011 opt_mult <callinfo!mid:*, argc:1, ARGS_SIMPLE>, <callcache> 0014 leave => nil
  36. class IntegerNode < Node attr_reader :value def initialize(value) @value =

    value end def bytecode(g) pos(g) g.push_int @value end end
  37. class IntegerNode < Node attr_reader :value def initialize(value) @value =

    value end def bytecode(g) pos(g) g.push_int @value end end
  38. class AddNode < Node attr_reader :left_operand, :right_operand def initialize(left_operand, right_operand)

    super @left_operand = left_operand @right_operand = right_operand end def bytecode(g) pos(g) @left_operand.bytecode(g) @right_operand.bytecode(g) g.send(:+, 1) end end