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

Elixirでプログラミング言語を作ろう #tokyoex / Let's Make Your Own Language in Elixir

Elixirでプログラミング言語を作ろう #tokyoex / Let's Make Your Own Language in Elixir

Kentaro Kuribayashi

November 20, 2021
Tweet

More Decks by Kentaro Kuribayashi

Other Decks in Technology

Transcript

  1. 2 ࣗݾ঺հ GMOϖύϘגࣜձࣾ ιϑτ΢ΣΞΤϯδχΞ ܀ྛ ݈ଠ࿠ a.k.a. ͋ΜͪΆ • GMOϖύϘऔక໾CTO

    • JAISTത࢜લظ՝ఔ • Twitter : @kentaro ElixirConf US 2021Ͱൃදͨ͠Γ͠·ͨ͠ɻ → ձࣾͷςοΫϒϩά / YouTubeͷಈը
  2. ͜ͷτʔΫʹࢸ͖͔͚ͬͨͬ 3 • ͖͔͚ͬ͸ɺWEB+DB PRESS Vol.125 ʢӈਤʣͷಛूΛಡΜͩ͜ͱ • पғʢࣾ಺ʣͰ΋ಠࣗݴޠΛ࡞ΔࢼΈ͕ ੝Γ্͕͍ͬͯΔ

    ɹˠ RBSʢԾʣʢby @udzuraʣ ɹˠ EVMͰಈ͘εϚʔτίϯτϥΫτهड़ݴޠΛ ࡞Γ͍ͨʢby @akht_ikdʣ ࣗ࡞ݴޠͷ੝Γ্͕Γ ӈਤͷग़ॴɿWEB+DB PRESS Vol.125ʛٕज़ධ࿦ࣾ
  3. 5 ͲΜͳݴޠ͔ʁʢ۩৅ߏจ͔Βʣ def factorial(n) { if (n < 2) {

    1 } else { n * factorial(n - 1) } } puts(factorial(5)) ࣜϕʔεɺ৚݅෼ذɾ܁Γฦ͠ɺؔ਺ݺͼग़͠ɺσʔλ͸੔਺ͱจࣈྻ for (i in 1 to 100) { if (i % 15 == 0) { puts("FizzBuzz") } else { if (i % 5 == 0) { puts("Buzz") } else { if (i % 3 == 0) { puts("Fizz") } else { puts(i) } } } } ※ ্ه͸ߨԋऀ͕࡞ͬͨݴޠʹΑΔίʔυྫͰ͋ΓɺWEB+DB PRESS Vol.125ͷಛूهࣄͰղઆ͞Ε͍ͯΔݴޠʢToysʣͱ͸΍΍ҟͳΔɻ
  4. 6 WEB+DB PRESSಛूͷըظతͳͱ͜Ζ ۩৅ߏจ͔ΒͰ͸ͳ͘ந৅ߏจ͔Βݴޠ࣮૷Λ࢝ΊΔ ۩৅ߏจ ந৅ߏจ ҙຯ࿦ Α͋͘Δઆ໌ͷྲྀΕ ந৅ߏจ ۩৅ߏจ

    ҙຯ࿦ ຊಛूͷઆ໌ͷྲྀΕ ͜ͷྲྀΕͷϝϦοτ ʮΠϯλϓϦλͷຊ࣭͸ந ৅ߏจ໦ʯʢp.21ʣ ۩৅ߏจ͔ΒೖΔͷ͸ɺ΍ Δ΂͖͜ͱ͕ෳࡶͰɺ࠳ં ͠΍͍͢ɻ ந৅ߏจ͔ΒೖΔํ͕ɺී ஈͷϓϩάϥϛϯάʹۙ͘ ͯͱ͖ͬͭ΍͍͢ʢͱࢥ͏ʣ ग़ॴɿWEB+DB PRESS Vol.125 p.21Λݩʹ࡞ਤͨ͠ɻ
  5. 7 ·ͣ࡞Γ࢝ΊΔՕॴ 1.࢛ଇԋࢉΛදݱ͠ಘΔந৅ߏจ໦Λɺ Structͱͯ͠ఆٛ͢Δ 2.ந৅ߏจ໦Λड͚औͬͯɺ࠶ىతʹղ ऍɾ࣮ߦ͢Δʢӈਤͷinterpretؔ ਺ʣ 3.ࣜʹؚ·ΕΔૢ࡞ͷछผʹԠͯ͡ॲཧ ͠Θ͚Δ 4.ͦͷૢ࡞ʹରԠ͢ΔElixirͷૢ࡞Λ࣮ߦ

    ͢Δ ந৅ߏจ໦ͷσʔλߏ଄ͱɺͦΕΛ࣮ߦ͢ΔΠϯλϓϦλ͔Β࡞Γ࢝ΊΔ def interpret(%Rorty.Ast.Expression.Binary{} = expr) do case expr.operator do Rorty.Operator.Add -> interpret(expr.left) + interpret(expr.right) Rorty.Operator.Divide -> interpret(expr.left) / interpret(expr.right) Rorty.Operator.Multiply -> interpret(expr.left) * interpret(expr.right) Rorty.Operator.Subtract -> interpret(expr.left) - interpret(expr.right) end end ࠶ؼ࣮ߦ
  6. 9 ۩৅ߏจΛ࡞͍ͬͯ͘ • ந৅ߏจΛ࡞͍ͬͯ͘ͱɺͦΕΛ࢖ͬͨϓϩάϥϜͦͷ΋ͷΛಈ͔͍ͨ͠ؾ࣋ͪʹͳͬͯ͘ ΔͷͰɺ͋Δఔ౓ͷͱ͜ΖͰ۩৅ߏจʹͱΓ͔͔Δ ◦ ࣮ࡍʹ͸ɺ࢛ଇԋࢉͷந৅ߏจΛ࡞ͬͨΒɺରԠ͢Δ۩৅ߏจΛఆٛ͠·ͨ͠ • ۩৅ߏจͷهड़ʹ͸PEGΛར༻ʢଞͷબ୒ࢶ͸BNFͱYeccɺNimbleParsec౳ʣɻ •

    ͦͷར఺͸ҎԼͷ௨Γ ͋Δఔ౓ந৅ߏจ͕Ͱ͖ͨΒɺύʔαδΣωϨʔλͰ۩৅ߏจΛ࡞Δ ग़ॴɿWEB+DB PRESS Vol.125 p.30 1. ߏจղੳͱࣈ۟ղੳΛ۠ผ͠ͳ͍ʢ͠ͳͯ͘Α͍ʣ 2. BNF͔Β୯७ʹߏจղੳثΛ࡞ͬͨ৔߹ͷ໘౗͍͘͞໰୊ʹ൥Θ͞Εͳ͍ 3. PEG͔ΒߏจղੳثΛ࡞Δͷ͸؆୯Ͱ͋Δ
  7. 11 Neotomexೖ໳ PEGʹΑΔจ๏هड़ΛElixirͷDSLͰॻ͚Δʂʢ਺ࣈΛύʔε͢Δྫʣ defmodule Number do use Neotomex.ExGrammar @root true

    define :number, "digit+" do digits -> digits |> Enum.join |> String.to_integer end define :digit, "[0-9]" end 42 = Number.parse! "42" ग़ॴɿjtmoulia/neotomex: A PEG parser/transformer with a pleasant Elixir DSL.
  8. 12 DSLʹΑΔจ๏ఆ͔ٛΒAST΁ 1.PEGͷจ๏͕DSLԽ͞Ε͍ͯΔͷ ͰɺͦΕʹԊͬͯݴޠͷจ๏ͱϚο νͨ࣌͠ͷૢ࡞Λॻ͍͍ͯ͘ 2.طʹ࡞ͬͨந৅ߏจʹϚον͢ΔΑ ͏จ๏Λఆٛͯ͠ɺ۩৅ߏจΛந৅ ߏจʹม׵͍ͯ͘͠ PEG-LikeͳDSLͰจ๏Λهड़͠ɺ۩৅ߏจΛந৅ߏจʹม׵͍ͯ͘͠ @root

    true define :additive, "multitive <'+'> additive / multitive" do int when is_struct(int, Rorty.Ast.Expr.IntegerLiteral) - > int [x, y] -> Rorty.Ast.add(x, y) end # ʢলུʣ define :decimal, "[0-9]+" do digits -> Enum.join(digits) |> String.to_integer() |> Rorty.Ast.integer() end ۩৅ߏจΛந৅ߏจ ʹม׵͢Δ
  9. 13 ग़དྷ্͕ͬͨ΋ͷΛ૊Έ߹ΘͤΔ ۩৅ߏจʹΑΓهड़͞ΕͨιʔείʔυΛந৅ߏจʹม׵ͯ͠ɺղऍɾ࣮ߦ def run(src) do {result, _} = src

    |> parse() |> Rorty.Interpreter.interpret(%Rorty.Env{}) result end def parse(src) do try do src |> String.trim() |> Rorty.Grammar.parse!() rescue e in Neotomex.Grammar.ParseError -> e |> handle_exception() end end
  10. 14 What's Next? • Erlang VMʢBEAMʣ্Ͱಈ͘ݴޠ͸BEAM Languagesͱݺ͹ΕΔ • ୅දతͳBEAM Languages͸Erlang/OTP΍Elixir

    • ଞɺLispʹӨڹΛड͚ͨ΋ͷ΍੩తܕ෇͚ͷ΋ͷͳͲɺগͳ͘ͱ΋33Ҏ্͕͋Δ ͜͜Ͱ঺հͨࣗ͠࡞ݴޠ΋ɺElixirͰ࣮ߦ͍ͯ͠ΔͷͰBEAM্Ͱಈ͍ͯ͸͍ΔͷͰɺ BEAM Languagesͱ͍͑͹͍͑Δ͚Ͳ……ɻ → ͡Ό͋ɺͪΌΜͱͨ͠BEAM Languagesͱ΍ΒΛ࡞ͬͯΈΑ͏͡Όͳ͍͔ ؆қͳΠϯλϓϦλݴޠ͔ΒYet Another BEAM Language΁ ग़ॴɿllaisdy/beam_languages: Languages, and about languages, on the BEAM
  11. 16 BEAM Languages΁޲͚ͯ 1.ࣗ࡞ݴޠͷASTΛElixirͷAST΁τϥϯε ύΠϧ͢Δʢӈ্ʣ 2.τϥϯεύΠϧͨ݁͠ՌΛɺ Code.eval_quoted/3Ͱ࣮ߦ ElixirͷASTΛ࢖͏ͱɺࣗ࡞ݴޠͷந৅ߏจΛElixir ʹௐ੔͠ͳ͚Ε͹ͳΒͳ͍͠ɺElixirͷҙຯ࿦ʹറΒ Εͯɺ݁ہElixirͷαϒηοτΛ࡞Δ͜ͱʹͳͬͯ͠

    ·͏ɻ → ࠓޙͲ͏͢Δ͔͸໎ͬͯΔͱ͜Ζ ·ͣ͸ࣗ࡞ݴޠͷASTΛElixirͷAST΁τϥϯεύΠϧͯ͠Έͨ defmodule Rorty.Transpiler do def transpile(exprs) do exprs |> Enum.map(fn expr -> Rorty.Protocol.ElixirAst.to_elixir_ast(expr) end) end end def run(src, transpile: true) do {result, _} = src |> parse() |> transpile() |> Code.eval_quoted([], __ENV__) result end
  12. 17 ·ͱΊ • ϓϩάϥϛϯάݴޠͷ࡞Γํʹ͍ͭͯઆ໌͍ͯ͠Δॻ੶΍هࣄ͸ͨ͘͞Μ͋Δ͕ɺ WEB+DB PRESS Vol.125ͷಛू͸ɺͦͷઆ໌ͷελΠϧʹ͓͍ͯग़৭ ◦ ந৅ߏจˠ۩৅ߏจͱ͍͏આ໌ͷྲྀΕ ◦

    ࣈ۟ղੳͱ͔BNFͱ͔ͦ͏͍͏࿩Λ͠ͳ͍ • جຊతʹɺͦ͜Ͱॻ͔Ε͍ͯΔ͜ͱΛຊτʔΫͰઆ໌ͨ͠Α͏ʹElixirతʹಡΈସ͑ͯ ࣮૷͍ͯ͘͠ͱɺϓϩάϥϛϯάݴޠͬΆ͍΋ͷ͕Ͱ͖Δ • ͜ͷઌ͸ɺૉ๿ͳΠϯλϓϦλ͔ΒBEAM Languages΁ঢ՚͍͖͍ͤͯͨ͞ͱ͜Ζ ElixirͰϓϩάϥϛϯάݴޠΛ࡞Ζ͏ʂ