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

Play with Rubys AST

makicamel
February 11, 2020

Play with Rubys AST

数学の勉強のために Ruby の構文木と遊んでいる話
2020.02.11. 大江戸 Ruby 会議 08

makicamel

February 11, 2020
Tweet

More Decks by makicamel

Other Decks in Programming

Transcript

  1. ਺ֶͷษڧͷͨΊʹ
    3VCZͷߏจ໦ͱ༡ΜͰ͍Δ࿩
    2020.2.11.
    େߐށRubyձٞ08
    @makicamel

    View Slide

  2. •@makicamel / ઒ݪສق

    •޷͖ͳ΋ͷ͸Ϗʔϧɹɹͱ͓ञ
    •ࠓ೥ޙ൒ͷ໨ඪ͸਺ֶͷษڧ
    ࣗݾ঺հ

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. ࠓ೔͸ 2 ݄ 11 ೔

    View Slide

  7. View Slide

  8. •@makicamel / ઒ݪສق

    •޷͖ͳ΋ͷ͸Ϗʔϧɹɹͱ͓ञ
    •ࠓ೥ޙ൒ͷ໨ඪ͸਺ֶͷษڧ
    ࣗݾ঺հ

    View Slide

  9. •਺ֶͰͭ·ͮ͘ग़དྷࣄ͕૿͖͑ͯͨ
    •CRuby ࣮૷ͱ͔ Kernel Ϟδϡʔϧ (ΔΓ·) ͱ͔

    ϓϩάϥϛϯάͷجૅ (ຊ) ͱ͔
    •ෳૉ਺ͱ͔ྦྷ৐ͱ͔ 2 ࣍ํఔࣜͱ͔ղͷެࣜͱ͔
    •௥͍ͬͯͨίʔυɾຊ్͕੾Εͯ͠·͏ͷ΋͍ͬͨͳ͍
    •ۤखҙࣝΛࠀ෰ͨ͠Β΋ͬͱָ͘͠ͳΓͦ͏
    ͳͥ਺ֶʁ

    View Slide

  10. 2 ࣍ํఔࣜ ͷ܎਺ 

    (͍ͣΕ΋࣮਺ͱ͢Δ) Λ༩͑ΒΕͨΒɺ

    ൑ผࣜͷ஋Λฦؔ͢਺ Λ

    σβΠϯϨγϐ※
    ʹ͕ͨͬͯ͠࡞Εɻ

    ͜͜Ͱ ͸ Ͱ͸ͳ͍ͱԾఆͯ͠Α͍ɻ
    ax2 + bx + c = 0 a, b, c
    hanbetsushiki
    a 0
    ϓϩάϥϛϯάͷجૅ $PNQVUFS4DJFODF-JCSBSZ

    ୈষؔ਺ͷఆٛcઙҪ݈Ұ

    IUUQTXXXBNB[PODPKQEQ
    ˞σβΠϯϨγϐͱ͸͜ͷຊͰఏএ͞Ε͍ͯΔɺϓϩάϥϜ࡞੒࣌ʹ͕ͨ͠͏΂͖ࢦ਑ͷ͜ͱ

    ͨͱ͑͹ʁ

    View Slide

  11. ɹɹɹɹɹɹɹɹ ͡Όͳͯ͘ɹɹɹɹ ͬͯॻ͖͍ͨ
    ͨͱ͑͹ʁ
    •൑ผࣜ
    •ํఔࣜ ʹରͯ͠ ͷ͜ͱ
    •Ruby Ͱॻ͘ͱ
    •௕͍
    •΋͏গ͠਺ࣜͬΆ͘ॻ͖͍ͨ
    ax2 + bx + c b2 − 4ac
    b ** 2 - 4 * a * c
    4 * a * c 4 a c

    View Slide

  12. Α͠ɺ࡞Ζ͏

    View Slide

  13. ࡞ͬͨ ͯΔ
    [email protected]@QBSTFSc(JU)VC

    [email protected]

    View Slide

  14. ࣮૷ํ਑
    •ͦ΋ͦ΋ɹɹɹɹ ͬͯॻ͘ͱͲ͏ͳΔ͔
    •γϯλοΫεΤϥʔ
    •Ϧςϥϧɾม਺͸ۭനจࣈ۠੾ΓͰฒ΂ΒΕͳ͍※
    •਺஋ͷޙʹ͓͚Δͷ͸ɹɹΈ͍ͨͳԋࢉࢠͳͲ
    4 a c
    ˞BMJBTΩʔϫʔυ΍ه๏ͱ͍ͬͨྫ֎͕͋Γ·͢
    *

    View Slide

  15. ࣮૷ํ਑
    •ͦ΋ͦ΋ɹɹɹɹ ͬͯॻ͘ͱͲ͏ͳΔ͔
    •γϯλοΫεΤϥʔ
    •Ϧςϥϧɾม਺͸ۭനจࣈ۠੾ΓͰฒ΂ΒΕͳ͍※
    •਺஋ͷޙʹ͓͚Δͷ͸ɹɹΈ͍ͨͳԋࢉࢠͳͲ
    4 a c
    ˞BMJBTΩʔϫʔυ΍ه๏ͱ͍ͬͨྫ֎͕͋Γ·͢
    *

    View Slide

  16. ࣮૷ํ਑
    Ruby ϓϩάϥϜͷ

    ߏจղੳΛ͢Δ

    View Slide

  17. ࣮૷ํ਑
    ɹRuby Ͱͭ͘Δ Ruby ͓΋͠Ζ͔ͬͨͰ͢Αʂ
    @neko314_

    View Slide

  18. Ruby Ͱͭ͘Δ Ruby
    •ԕ౻ (@mame) ͞Μͷஶ࡞
    •ʮRuby Ͱ Ruby Λ࡞Γͳ͕ΒRuby
    ΛֶͿʯຊ
    •Ruby Ͱ Ruby ΠϯλʔϓϦλΛ࡞Δ
    3VCZͰͭ͘Δ3VCZθϩ͔Βֶͼͳ͓͢ϓϩάϥϛϯάݴޠೖ໳cԕ౻ါհ

    IUUQTXXXMBNCEBOPUFDPNQSPEVDUTSVCZSVCZ
    ղઆ΋Θ͔Γ΍ͯ͘͢

    ֆ΋͔Θ͍ͯ͘ͱͯ΋ͨͷ͍͠

    View Slide

  19. ࣈ۟ղੳ
    ߏจղੳ
    ࣮ߦʢܭࢉʣ
    Ruby Ͱͭ͘Δ Ruby

    View Slide

  20. ࣈ۟ղੳ
    •ɹɹɹɹɹɹ ͱ͍͏ Ruby ϓϩάϥϜ͸
    4 * a * c
    def hello; "World"; end
    def hello ; “ World " ; end
    ࣈ۟ = Token (τʔΫϯ)
    4 * a * c
    ɹɹͱ͍͏ࣈ۟ʹ෼͚ΒΕΔ
    •ɹɹɹɹɹɹɹɹɹɹɹɹɹɹͱ͍͏ Ruby ϓϩάϥϜ͸
    ɹɹͱ͍͏ࣈ۟ʹ෼͚ΒΕΔ

    View Slide

  21. ࣈ۟ղੳ
    •ࣈ۟Λղੳͯ͠ҙຯ͚ͮ͢Δ
    •਺஋ɺԋࢉࢠɺจࣈྻɺࣝผࢠͳͲͳͲ
    •ࣈ۟ղੳ = Tokenize (τʔΫφΠζ)
    4 * a * c
    ਺஋ ԋࢉࢠ ԋࢉࢠ
    ࣝผࢠ ࣝผࢠ

    View Slide

  22. ߏจղੳ
    •ਓ͕ؒɹɹɹ
    4 * a * c
    4 * a * c ΛಡΉ࣌

    View Slide

  23. ߏจղੳ
    •ΠϯλʔϓϦλ͕ɹɹɹɹɹɹɹΛಡΉ࣌
    4 * a * c

    View Slide

  24. ߏจղੳ
    •ΠϯλʔϓϦλ͕ɹɹɹɹɹΛಡΉ࣌
    *
    4 * a
    4 * a

    View Slide

  25. ߏจղੳ
    •ΠϯλʔϓϦλ͕ɹɹɹɹɹɹɹΛಡΉ࣌
    *
    4 *
    a * c
    ൓స
    4 * a * c

    View Slide

  26. ߏจղੳ
    •ΠϯλʔϓϦλ͕ɹɹɹɹɹɹɹΛಡΉ࣌
    4 * *
    *
    a * c
    ߏจ໦
    4 * a * c

    View Slide

  27. ߏจղੳ
    •ΠϯλʔϓϦλ͕ɹɹɹɹɹɹɹΛಡΉ࣌
    4 * *
    *
    a * c
    4 * a * c
    •ߏจ໦ʹม׵͢Δ͜ͱ = ߏจղੳ = Parse
    •ղੳͷ్தͰਖ਼͍͠ߏจ͔νΣοΫ͢Δ

    View Slide

  28. ߏจղੳ
    ͜Μͳߏจ໦͕ಘΒΕͨ࣌ʹɹɹΛೖΕͨΒ͍͚ΔͷͰ͸…ʂʂ
    *
    4
    a

    View Slide

  29. ࣮૷ํ਑
    •ͦ΋ͦ΋ɹɹɹɹ ͬͯॻ͘ͱͲ͏ͳΔ͔
    •γϯλοΫεΤϥʔ
    •Ϧςϥϧɾม਺͸ۭനจࣈ۠੾ΓͰฒ΂ΒΕͳ͍※
    •਺஋ͷޙʹ͓͚Δͷ͸ɹɹΈ͍ͨͳԋࢉࢠͳͲ
    4 a c
    ˞BMJBTΩʔϫʔυ΍ه๏ͱ͍ͬͨྫ֎͕͋Γ·͢
    *

    View Slide

  30. ߏจղੳ
    ͜Μͳߏจ໦͕ಘΒΕͨ࣌ʹɹɹΛೖΕͨΒ͍͚ΔͷͰ͸…ʂʂ
    *
    4
    a

    ͜Μͳߏจ໦͸ଘࡏ͠ͳ͍

    View Slide

  31. ߏจղੳ
    •ɹɹɹɹɹɹͱ͍͏τʔΫϯ͕࿈ଓͯ͠ݱΕͨΒ
    ɹɹɹΛૠೖͯ͠ฦ͢
    4 a
    ਺஋ ࣝผࢠ
    *

    View Slide

  32. Ripper.lex
    TJOHMFUPONFUIPE3JQQFSMFYc3VCZϦϑΝϨϯεϚχϡΞϧ

    IUUQTEPDTSVCZMBOHPSHKBNFUIPE3JQQFSTMFYIUNM

    View Slide

  33. Ripper.lex
    Ripper.lex('4 * a')
    # => [[[1, 0], :on_int, "4", END],
    # [[1, 1], :on_sp, " ", END],
    # [[1, 2], :on_op, "*", BEG],
    # [[1, 3], :on_sp, " ", BEG],
    # [[1, 4], :on_ident, "a", ARG]]
    Ґஔ৘ใ

    View Slide

  34. Ripper.lex
    Ripper.lex('4 * a')
    # => [[[1, 0], :on_int, "4", END],
    # [[1, 1], :on_sp, " ", END],
    # [[1, 2], :on_op, "*", BEG],
    # [[1, 3], :on_sp, " ", BEG],
    # [[1, 4], :on_ident, "a", ARG]]
    ੔਺
    ۭനจࣈ
    ԋࢉࢠ
    ࣝผࢠ

    View Slide

  35. Ripper.lex
    Ripper.lex('4 * a')
    # => [[[1, 0], :on_int, "4", END],
    # [[1, 1], :on_sp, " ", END],
    # [[1, 2], :on_op, "*", BEG],
    # [[1, 3], :on_sp, " ", BEG],
    # [[1, 4], :on_ident, "a", ARG]]
    τʔΫϯจࣈྻ

    View Slide

  36. Ripper.lex
    Ripper.lex('4 * a')
    # => [[[1, 0], :on_int, "4", END],
    # [[1, 1], :on_sp, " ", END],
    # [[1, 2], :on_op, "*", BEG],
    # [[1, 3], :on_sp, " ", BEG],
    # [[1, 4], :on_ident, "a", ARG]]
    State

    View Slide

  37. Ripper::Lexer::State
    Ripper::Lexer.constants.select do |constant|
    constant.to_s.start_with? :EXPR.to_s
    end
    # => [
    :EXPR_NONE,
    :EXPR_BEG, :EXPR_END, :EXPR_ENDARG, :EXPR_ENDFN,
    :EXPR_ARG, :EXPR_CMDARG, :EXPR_MID, :EXPR_FNAME,
    :EXPR_DOT, :EXPR_CLASS, :EXPR_LABEL, :EXPR_LABELED,
    :EXPR_FITEM, :EXPR_VALUE,
    :EXPR_BEG_ANY, :EXPR_ARG_ANY, :EXPR_END_ANY
    ]
    •τʔΫϯͷ State

    View Slide

  38. class RDoc::Parser::RipperStateLex
    class InnerStateLex < Ripper::Filter
    private def on_variables(event, tok, data)
    if @in_fname
    @lex_state = EXPR_ENDFN
    @in_fname = false
    @continue = false
    elsif @continue
    case @lex_state
    when EXPR_DOT
    @lex_state = EXPR_ARG
    else
    @lex_state = EXPR_ENDFN
    @continue = false
    end
    else
    @lex_state = EXPR_CMDARG
    end
    data << Token.new(lineno, column, event, tok, @lex_state)
    end
    # ...
    end
    •ϓϩάϥϜͷͲ͜ʹ͋Δ͔ʹΑͬͯState͕มԽ͢Δ

    View Slide

  39. ܭࢉͷ࣮ߦ
    def evaluate(tree, genv, lenv)
    case tree[0]
    when "lit"
    tree[1]
    when "+"
    evaluate(tree[1], genv, lenv) + evaluate(tree[2], genv, lenv)
    when "-"
    evaluate(tree[1], genv, lenv) - evaluate(tree[2], genv, lenv)
    when "*"
    evaluate(tree[1], genv, lenv) * evaluate(tree[2], genv, lenv)
    when "/"
    evaluate(tree[1], genv, lenv) / evaluate(tree[2], genv, lenv)
    when "**"
    evaluate(tree[1], genv, lenv)**evaluate(tree[2], genv, lenv)
    when "%"
    evaluate(tree[1], genv, lenv) % evaluate(tree[2], genv, lenv)
    when "evaluate(tree[1], genv, lenv) < evaluate(tree[2], genv, lenv)
    when "<="
    evaluate(tree[1], genv, lenv) <= evaluate(tree[2], genv, lenv)
    when ">"
    # ...
    3VCZͰͭ͘Δ3VCZθϩ͔Βֶͼͳ͓͢ϓϩάϥϛϯάݴޠೖ໳cԕ౻ါհ

    IUUQTXXXMBNCEBOPUFDPNQSPEVDUTSVCZSVCZ

    View Slide

  40. खॱ
    1. Ripper ʹ͔͚ͯߏจղੳ͕Ͱ͖Δ͔νΣοΫ
    2. Ͱ͖ͳ͚Ε͹਺஋ͱม਺͕ฒΜͰ͍ΔจࣈྻΛ୳͢
    3. ਺஋ͱม਺ͷؒʹ * Λૠೖͯ͠ฦ͢
    4. 1 ʙ 3 ͷ܁Γฦ͠
    5. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ
    ※ ߏจղੳ͸

    Ruby Ͱͭ͘Δ Ruby ͷิॿϥΠϒϥϦ

    minrubyΛ࢖͍·͢
    NJOSVCZNBNFNJOSVCZc(JU)VC

    IUUQTHJUIVCDPNNBNFNJOSVCZ

    View Slide

  41. खॱ
    1. Ripper ʹ͔͚ͯߏจղੳ͕Ͱ͖Δ͔νΣοΫ
    2. Ͱ͖ͳ͚Ε͹਺஋ͱม਺͕ฒΜͰ͍ΔจࣈྻΛ୳͢
    3. ਺஋ͱม਺ͷؒʹ * Λૠೖͯ͠ฦ͢
    4. 1 ʙ 3 ͷ܁Γฦ͠
    5. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ
    NJOSVCZNBNFNJOSVCZc(JU)VC

    IUUQTHJUIVCDPNNBNFNJOSVCZ

    View Slide

  42. Ripper.sexp('4 * a')
    # => [:program,
    # [[:binary,
    # [:@int, "4", [1, 0]],
    # :*,
    # [:vcall, [:@ident, "a", [1, 4]]]]]]
    Ripper.sexp
    program͔Β࢝·ΔϦετ
    ೋ෼໦
    ਺஋Ϧςϥϧ
    ৐ࢉԋࢉࢠ
    ࣝผࢠ

    View Slide

  43. Ripper.sexp('4 a')
    # => nil
    Ripper.sexp

    View Slide

  44. Ripper.sexp
    class Ripper
    def Ripper.sexp(src, filename = '-', lineno = 1)
    builder = SexpBuilderPP.new(src, filename, lineno)
    sexp = builder.parse
    sexp unless builder.error?
    end
    # ...
    end
    DMBTT3JQQFSSVCZSVCZc(JU)VC

    IUUQTHJUIVCDPNSVCZSVCZCMPCGBCFYUSJQQFSMJCSJQQFSTFYQSC

    View Slide

  45. ߏจղੳνΣοΫ
    1. Ripper ʹ͔͚ͯߏจղੳ͕Ͱ͖Δ͔νΣοΫ
    module MathParser
    class MathRubyParser < MinRubyParser
    def mathruby_parse(program)
    converted_program = Ripper.sexp(program) ?
    program : reparse(program.strip)
    simplify(Ripper.sexp(converted_program))
    end
    # ...
    end
    end

    View Slide

  46. τʔΫϯνΣοΫ
    2. Ͱ͖ͳ͚Ε͹਺஋ͱม਺͕ฒΜͰ͍ΔจࣈྻΛ୳͢
    module MathParser::Converter
    def reparse(program)
    tokens = Ripper.lex(program)
    converted_program = convert(tokens)
    original_program = tokens.map{|token| token[2]}.join ""
    program.sub!(original_program, converted_program)
    Ripper.sexp(program).nil? ? reparse(program) : program
    end
    # ...
    end

    View Slide

  47. module MathParser::Converter
    def convert(tokens)
    lines = []
    tokens.last[0][0].times do |i|
    line_no = i + 1
    previous_numeric = false
    line = []
    tokens.map do |token|
    next if line_no > token[0][0]
    break if line_no < token[0][0]
    if space?(token)
    line << token[2]
    elsif currently_numeric?(token) && previous_numeric
    previous_numeric = true
    line << ["*", token[2]]
    else
    previous_numeric = currently_numeric?(token)
    line << token[2]
    end
    end
    lines << line.join("")
    end
    lines.join("")
    end
    # ...
    end
    ߦ൪߸ͷॳظԽ(1͔Β։࢝)
    ߦ൪߸͕ҧͬͨΒεΩοϓ
    line ʹτʔΫϯΛ௥Ճ

    View Slide

  48. module MathParser::Converter
    def convert(tokens)
    lines = []
    tokens.last[0][0].times do |i|
    line_no = i + 1
    previous_numeric = false
    line = []
    tokens.map do |token|
    next if line_no > token[0][0]
    break if line_no < token[0][0]
    if space?(token)
    line << token[2]
    elsif currently_numeric?(token) && previous_numeric
    previous_numeric = true
    line << ["*", token[2]]
    else
    previous_numeric = currently_numeric?(token)
    line << token[2]
    end
    end
    lines << line.join("")
    end
    lines.join("")
    end
    # ...
    end

    View Slide

  49. τʔΫϯνΣοΫ
    2. Ͱ͖ͳ͚Ε͹਺஋ͱม਺͕ฒΜͰ͍ΔจࣈྻΛ୳͢
    module MathParser::Converter
    def currently_numeric?(token)
    ref_variable?(token) || int?(token) ||
    float?(token) || rational?(token)
    end
    def ref_variable?(token)
    (token[1] == :on_ident) &&
    (EXPR_END | EXPR_LABEL) == token[3])
    end
    def int?(token)
    token[1] == :on_int
    end
    # ...
    end

    View Slide

  50. τʔΫϯνΣοΫ
    2. Ͱ͖ͳ͚Ε͹਺஋ͱม਺͕ฒΜͰ͍ΔจࣈྻΛ୳͢
    module MathParser::Converter
    def currently_numeric?(token)
    ref_variable?(token) || int?(token) ||
    float?(token) || rational?(token)
    end
    def ref_variable?(token)
    (token[1] == :on_ident) &&
    (EXPR_END | EXPR_LABEL) == token[3])
    end
    def int?(token)
    token[1] == :on_int
    end
    # ...
    end

    View Slide

  51. module MathParser::Converter
    def convert(tokens)
    lines = []
    tokens.last[0][0].times do |i|
    # ...
    tokens.map do |token|
    # ...
    if space?(token)
    line << token[2]
    elsif currently_numeric?(token) && previous_numeric
    previous_numeric = true
    line << ["*", token[2]]
    else
    previous_numeric = currently_numeric?(token)
    line << token[2]
    end
    end
    lines << line.join("")
    end
    # ...
    * ͷૠೖ
    3. ਺஋ͱม਺ͷؒʹ * Λૠೖͯ͠ฦ͢

    View Slide

  52. 4. 1 ʙ 3 ͷ܁Γฦ͠
    * ͷૠೖ
    module MathParser::Converter
    def reparse(program)
    tokens = Ripper.lex(program)
    converted_program = convert(tokens)
    original_program = tokens.map{|token| token[2]}.join ""
    program.sub!(original_program, converted_program)
    Ripper.sexp(program).nil? ? reparse(program) : program
    end
    # ...
    end

    View Slide

  53. 4. 1 ʙ 3 ͷ܁Γฦ͠
    Ripper.lex '1 2 3 4 5'
    # => [[[1, 0], :on_int, "1", END],
    # [[1, 1], :on_sp, " ", END],
    # [[1, 2], :on_idt, "2", END]]
    γϯλοΫεΤϥʔʹͳΔͱlex్͕தͰऴྃ͢Δ
    * ͷૠೖ
    program.sub!(original_program, converted_program)
    Ripper.sexp(program).nil? ? reparse(program) : program

    View Slide

  54. ܭࢉͷ࣮ߦ
    module MathParser
    class MathRubyParser < MinRubyParser
    def mathruby_parse(program)
    converted_program = Ripper.sexp(program) ?
    program : reparse(program.strip)
    simplify(Ripper.sexp(converted_program))
    end
    # ...
    end
    end
    5. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ
    minruby ͷఏڙϝιου

    ߏจ໦Λऔಘͯ͘͠ΕΔ

    View Slide

  55. ܭࢉͷ࣮ߦ
    5. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ
    module MathParser
    def evaluate(tree, genv, lenv)
    case tree[0]
    when "lit"
    tree[1]
    when "+"
    evaluate(tree[1], genv, lenv) + evaluate(tree[2], genv, lenv)
    when "-"
    evaluate(tree[1], genv, lenv) - evaluate(tree[2], genv, lenv)
    when "*"
    evaluate(tree[1], genv, lenv) * evaluate(tree[2], genv, lenv)
    when "/"
    evaluate(tree[1], genv, lenv) / evaluate(tree[2], genv, lenv)
    when "**"
    evaluate(tree[1], genv, lenv)**evaluate(tree[2], genv, lenv)
    when "%"
    evaluate(tree[1], genv, lenv) % evaluate(tree[2], genv, lenv)
    when "evaluate(tree[1], genv, lenv) < evaluate(tree[2], genv, lenv)
    when "<="
    evaluate(tree[1], genv, lenv) <= evaluate(tree[2], genv, lenv)
    when ">"
    # ...
    3VCZͰͭ͘Δ3VCZθϩ͔Βֶͼͳ͓͢ϓϩάϥϛϯάݴޠೖ໳cԕ౻ါհ

    IUUQTXXXMBNCEBOPUFDPNQSPEVDUTSVCZSVCZ
    evaluate ϝιου͸

    Ruby Ͱͭ͘Δ Ruby ͷ಺༰΄΅ͦͷ··

    View Slide

  56. ࣮ߦͯ͠ΈΔ

    View Slide

  57. Ͱ͖ͨʂʂ
    irb(main):001:0> require ‘math_parser'
    => true
    irb(main):002:0' mp '
    irb(main):003:0' a = 2
    irb(main):004:0' c = 3
    irb(main):005:0' 4 a c
    irb(main):006:0> '
    => 24

    View Slide

  58. irb(main):001:0> require ‘math_parser'
    => true
    irb(main):002:0' mp '
    irb(main):003:0' a = 2
    irb(main):004:0' c = 3
    irb(main):005:0' a 4 c
    irb(main):006:0> '
    Ͱ͖ͯͳ͍
    ϝιουݺͼग़͠ͱͯ͠ղऍ͞ΕΔ
    NoMethodError (undefined method `[]' for nil:NilClass)

    View Slide

  59. module MathParser
    def evaluate(tree, genv, lenv)
    case tree[0]
    # ...
    when "func_call"
    args = tree[2..].map { |t| evaluate(t, genv, lenv) }
    # ૊ΈࠐΈؔ਺
    if respond_to?(tree[1]) || Kernel.respond_to?(tree[1])
    send(tree[1], *args)
    # Ϣʔβఆٛؔ਺
    elsif genv.has_key? tree[1]
    func_lenv = {}
    mhd = genv[tree[1]]
    params = mhd[1]
    params.each_with_index { |param, j| func_lenv[param] = args[j] }
    evaluate(mhd[2], genv, func_lenv)
    # ະఆٛͷ৔߹͸ tree Λมߋͯ͠࠶౓ evaluate ͢Δ
    else
    new_tree = ["*", ["var_ref", tree[1]], tree[2]]
    evaluate(new_tree, genv, lenv)
    end
    # ...
    end
    end
    end
    5. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ
    Ϣʔβఆٛؔ਺Λొ࿥

    View Slide

  60. Ͱ͖ͨʂʂ
    irb(main):001:0> require ‘math_parser'
    => true
    irb(main):002:0' mp '
    irb(main):003:0' a = 2
    irb(main):004:0' c = 3
    irb(main):005:0' a 4 c
    irb(main):006:0> '
    => 24

    View Slide

  61. Ͱ͖ͯͳ͍
    ͪΌΜͱͨ͠ΤϥʔΛ

    ฦ͢Α͏ʹ͍ͨ͠ͳ͋
    ෼਺ͱ͔ྦྷ৐ͱ͔

    ΋ͬͱײ֮తʹ

    ॻ͚ΔΑ͏ʹ͍ͨ͠ͳ͋
    ϝιου಺Ͱ͔͠ม਺

    ͍֮͑ͯΒΕͳ͍
    ͙͢Τϥʔग़Δ
    irb Έ͍ͨͳ REPL ্Ͱ

    ϝιουհͣ͞

    ࣮ߦͰ͖ΔΑ͏ʹ͍ͨ͠
    ●ɹ●
    ָ͠Έ

    View Slide

  62. ߏจղੳ͸ා͘ͳ͍
    ΊͪΌͪ͘Ό஍ಓ
    Ruby ͍͢͝
    Did you mean? ͍͢͝
    ͍͢͝΋ͷ͍ͬͺ͍͋Δ
    ·ͱΊ

    View Slide

  63. ࣗ෼Ͱ࢖͏ಓ۩

    ͭ͘Δͷָ͍͠ʂ
    ·ͱΊ

    View Slide

  64. Special Thanks
    Ruby Ͱͭ͘Δ Ruby | ԕ౻ ါհ (@mame) 

    https://www.lambdanote.com/products/ruby-ruby
    minruby | ԕ౻ ါհ (@mame) 

    https://github.com/mame/minruby

    View Slide

  65. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View Slide