Play with Rubys AST

Cfbe23392787cb3ad4689c5f72463fcc?s=47 makicamel
February 11, 2020

Play with Rubys AST

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

Cfbe23392787cb3ad4689c5f72463fcc?s=128

makicamel

February 11, 2020
Tweet

Transcript

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

  2. •@makicamel / ઒ݪສق • •޷͖ͳ΋ͷ͸Ϗʔϧɹɹͱ͓ञ •ࠓ೥ޙ൒ͷ໨ඪ͸਺ֶͷษڧ ࣗݾ঺հ

  3. None
  4. None
  5. None
  6. ࠓ೔͸ 2 ݄ 11 ೔

  7. None
  8. •@makicamel / ઒ݪສق • •޷͖ͳ΋ͷ͸Ϗʔϧɹɹͱ͓ञ •ࠓ೥ޙ൒ͷ໨ඪ͸਺ֶͷษڧ ࣗݾ঺հ

  9. •਺ֶͰͭ·ͮ͘ग़དྷࣄ͕૿͖͑ͯͨ •CRuby ࣮૷ͱ͔ Kernel Ϟδϡʔϧ (ΔΓ·) ͱ͔
 ϓϩάϥϛϯάͷجૅ (ຊ) ͱ͔

    •ෳૉ਺ͱ͔ྦྷ৐ͱ͔ 2 ࣍ํఔࣜͱ͔ղͷެࣜͱ͔ •௥͍ͬͯͨίʔυɾຊ్͕੾Εͯ͠·͏ͷ΋͍ͬͨͳ͍ •ۤखҙࣝΛࠀ෰ͨ͠Β΋ͬͱָ͘͠ͳΓͦ͏ ͳͥ਺ֶʁ
  10. 2 ࣍ํఔࣜ ͷ܎਺ 
 (͍ͣΕ΋࣮਺ͱ͢Δ) Λ༩͑ΒΕͨΒɺ
 ൑ผࣜͷ஋Λฦؔ͢਺ Λ
 σβΠϯϨγϐ※ ʹ͕ͨͬͯ͠࡞Εɻ


    ͜͜Ͱ ͸ Ͱ͸ͳ͍ͱԾఆͯ͠Α͍ɻ ax2 + bx + c = 0 a, b, c hanbetsushiki a 0 ϓϩάϥϛϯάͷجૅ $PNQVUFS4DJFODF-JCSBSZ ୈষؔ਺ͷఆٛcઙҪ݈Ұ
 IUUQTXXXBNB[PODPKQEQ ˞σβΠϯϨγϐͱ͸͜ͷຊͰఏএ͞Ε͍ͯΔɺϓϩάϥϜ࡞੒࣌ʹ͕ͨ͠͏΂͖ࢦ਑ͷ͜ͱ ͨͱ͑͹ʁ
  11. ɹɹɹɹɹɹɹɹ ͡Όͳͯ͘ɹɹɹɹ ͬͯॻ͖͍ͨ ͨͱ͑͹ʁ •൑ผࣜ •ํఔࣜ ʹରͯ͠ ͷ͜ͱ •Ruby Ͱॻ͘ͱ

    •௕͍ •΋͏গ͠਺ࣜͬΆ͘ॻ͖͍ͨ ax2 + bx + c b2 − 4ac b ** 2 - 4 * a * c 4 * a * c 4 a c
  12. Α͠ɺ࡞Ζ͏

  13. ࡞ͬͨ ͯΔ NBUI@QBSTFSNBLJDBNFMNBUI@QBSTFSc(JU)VC
 IUUQTHJUIVCDPNNBLJDBNFMNBUI@QBSTFS

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

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

    *
  16. ࣮૷ํ਑ Ruby ϓϩάϥϜͷ
 ߏจղੳΛ͢Δ

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

  18. Ruby Ͱͭ͘Δ Ruby •ԕ౻ (@mame) ͞Μͷஶ࡞ •ʮRuby Ͱ Ruby Λ࡞Γͳ͕ΒRuby

    ΛֶͿʯຊ •Ruby Ͱ Ruby ΠϯλʔϓϦλΛ࡞Δ 3VCZͰͭ͘Δ3VCZθϩ͔Βֶͼͳ͓͢ϓϩάϥϛϯάݴޠೖ໳cԕ౻ါհ
 IUUQTXXXMBNCEBOPUFDPNQSPEVDUTSVCZSVCZ ղઆ΋Θ͔Γ΍ͯ͘͢
 ֆ΋͔Θ͍ͯ͘ͱͯ΋ͨͷ͍͠
  19. ࣈ۟ղੳ ߏจղੳ ࣮ߦʢܭࢉʣ Ruby Ͱͭ͘Δ Ruby

  20. ࣈ۟ղੳ •ɹɹɹɹɹɹ ͱ͍͏ Ruby ϓϩάϥϜ͸ 4 * a * c

    def hello; "World"; end def hello ; “ World " ; end ࣈ۟ = Token (τʔΫϯ) 4 * a * c ɹɹͱ͍͏ࣈ۟ʹ෼͚ΒΕΔ •ɹɹɹɹɹɹɹɹɹɹɹɹɹɹͱ͍͏ Ruby ϓϩάϥϜ͸ ɹɹͱ͍͏ࣈ۟ʹ෼͚ΒΕΔ
  21. ࣈ۟ղੳ •ࣈ۟Λղੳͯ͠ҙຯ͚ͮ͢Δ •਺஋ɺԋࢉࢠɺจࣈྻɺࣝผࢠͳͲͳͲ •ࣈ۟ղੳ = Tokenize (τʔΫφΠζ) 4 * a

    * c ਺஋ ԋࢉࢠ ԋࢉࢠ ࣝผࢠ ࣝผࢠ
  22. ߏจղੳ •ਓ͕ؒɹɹɹ 4 * a * c 4 * a

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

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

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

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

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

    * a * c •ߏจ໦ʹม׵͢Δ͜ͱ = ߏจղੳ = Parse •ղੳͷ్தͰਖ਼͍͠ߏจ͔νΣοΫ͢Δ
  28. ߏจղੳ ͜Μͳߏจ໦͕ಘΒΕͨ࣌ʹɹɹΛೖΕͨΒ͍͚ΔͷͰ͸…ʂʂ * 4 a

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

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

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

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

  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]] Ґஔ৘ใ
  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]] ੔਺ ۭനจࣈ ԋࢉࢠ ࣝผࢠ
  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]] τʔΫϯจࣈྻ
  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
  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
  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͕มԽ͢Δ
  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
  40. खॱ 1. Ripper ʹ͔͚ͯߏจղੳ͕Ͱ͖Δ͔νΣοΫ 2. Ͱ͖ͳ͚Ε͹਺஋ͱม਺͕ฒΜͰ͍ΔจࣈྻΛ୳͢ 3. ਺஋ͱม਺ͷؒʹ * Λૠೖͯ͠ฦ͢

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

    4. 1 ʙ 3 ͷ܁Γฦ͠ 5. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ NJOSVCZNBNFNJOSVCZc(JU)VC
 IUUQTHJUIVCDPNNBNFNJOSVCZ
  42. Ripper.sexp('4 * a') # => [:program, # [[:binary, # [:@int,

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

  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
  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
  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
  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 ʹτʔΫϯΛ௥Ճ
  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
  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
  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
  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. ਺஋ͱม਺ͷؒʹ * Λૠೖͯ͠ฦ͢
  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
  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
  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 ͷఏڙϝιου
 ߏจ໦Λऔಘͯ͘͠ΕΔ
  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 ͷ಺༰΄΅ͦͷ··
  56. ࣮ߦͯ͠ΈΔ

  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
  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)
  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. * ͕ૠೖ͞Εͨߏจ໦Λ࣮ߦ (ܭࢉ) ͢Δ Ϣʔβఆٛؔ਺Λొ࿥
  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
  61. Ͱ͖ͯͳ͍ ͪΌΜͱͨ͠ΤϥʔΛ
 ฦ͢Α͏ʹ͍ͨ͠ͳ͋ ෼਺ͱ͔ྦྷ৐ͱ͔
 ΋ͬͱײ֮తʹ
 ॻ͚ΔΑ͏ʹ͍ͨ͠ͳ͋ ϝιου಺Ͱ͔͠ม਺
 ͍֮͑ͯΒΕͳ͍ ͙͢Τϥʔग़Δ irb

    Έ͍ͨͳ REPL ্Ͱ
 ϝιουհͣ͞
 ࣮ߦͰ͖ΔΑ͏ʹ͍ͨ͠ •ɹ• ָ͠Έ
  62. ߏจղੳ͸ා͘ͳ͍ ΊͪΌͪ͘Ό஍ಓ Ruby ͍͢͝ Did you mean? ͍͢͝ ͍͢͝΋ͷ͍ͬͺ͍͋Δ ·ͱΊ

  63. ࣗ෼Ͱ࢖͏ಓ۩
 ͭ͘Δͷָ͍͠ʂ ·ͱΊ

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


    https://www.lambdanote.com/products/ruby-ruby minruby | ԕ౻ ါհ (@mame) 
 https://github.com/mame/minruby
  65. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠