LoginSignup
6
1

More than 5 years have passed since last update.

ElixirでElixirインタプリタを作成していく(第1回 文字列からモジュールと関数を導き出す)

Last updated at Posted at 2019-03-14

ElixirでElixirインタプリタを作成していく

第1回 文字列からモジュールと関数を導き出す

本記事はElixirでElixirのインタプリタを作成していく.
その中で文字列中のモジュールと関数を抽出していくことを目標とする

文字列をモジュールと関数,および引数に分割する

今回は文字列をモジュールと関数,および引数に分割する関数を作成する.そのことより以下の関数を定義することを目的とする

定義

関数名:to_function
引数:String
返り値:%{module: Module, function: Atom, argments: String}
ここでモジュールがModule,関数がAtomであるのはapply関数を用いる際に都合が良いからである

実装

初めにElixirの関数は次のような記述を行う.

String.split "hello world"
# ["hello", "world"]

又は

String.split("hello world")

と記述する

今回は上記をメインの記述として開発を進めていく
すると確実にモジュールと関数の集合体と引数の二つの部分に分割することが可能なことに気づくだろう

そう,最初のスペースで分割すれば大まかな二つの部分に分割が可能だ
よって次のようになる

core.ex
defmodule ElixirInterpreter.Core do
  def to_function(str) when is_binary(str) do
    [order, args] = str |> String.split(" ", parts: 2)
  end
end

このうち,分割した2つ目の要素はすでに引数のみの文字列となっているためこのまま返せばいいことがわかる
次に,モジュールと関数を分割していく,その中でString.split

String.split # =>
Elixir.String.split

である.つまり,モジュール内にモジュールは定義されるが,関数内にモジュールは定義されない.
よって.で区切った文字列のうち一番最後が関数であり,それ以外はモジュールである.
つまり.で区切ったのち,最後とそれ以前を分けることができればよい.
次のようになる

core.ex
defmodule ElixirInterpreter.Core do
  def to_function(str) when is_binary(str) do
    [order, args] = str |> String.split(" ", parts: 2)

    {func, module} =
      order
      |> String.split(".")
      |> List.pop_at(-1)
   end
end

これで関数を文字列として取り出すことができた
残りはモジュール部分をModule,関数部分をAtomとして返すことができれば目的を達成することになる
関数部分は簡単である
funcとして帰ってきた部分をString.to_atomを用いることでAtomにすればよい
ところでmoduleとして帰ってきた部分を考えてみよう
これは文字列のリストとなっていることに気づくだろうか
String.splitが元になっている場合,["String"],
Elixir.String.splitが元の場合,["Elixir", "String"]となっている
この形式からModuleを直接求められる関数が存在する.
それはModule.concatである.この関数は文字列およびアトムのリストを引数として,それにあうModuleとして返す関数となっている
このことから最終的なto_function関数の形式が次のようになる

core.ex
defmodule ElixirInterpreter.Core do
  def to_function(str) when is_binary(str) do
    [order, args] = str |> String.split(" ", parts: 2)

    {func, module} =
      order
      |> String.split(".")
      |> List.pop_at(-1)

    module = Module.concat(module)
    func = String.to_atom(func)
    %{module: module, function: func, argments: args}
  end
end

これを実行すると次のようになる

iex(1)> ElixirInterpreter.Core.to_function "String.split "hello world™"
%{argments: ""hello world™", function: :split, module: String}
iex(2)> ElixirInterpreter.Core.to_function "Enum.reverse [1, 2, 3]"
%{argments: "[1, 2, 3]", function: :reverse, module: Enum}

定義どおりに実装できていることが確認できる


今回は文字列からモジュールと関数を導き出した.これはまだ始まったばかりだ.
次回からは引数部分をリストに変換するが現時点で厳しい道のりなのは明白である.

第0回 目次
第2回 基本的な引数の分割

以下ソースコード(マスターでゴリゴリ開発してるので悪しからず.)
GitHub: ElixirInterpreter

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1