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")
と記述する
今回は上記をメインの記述として開発を進めていく
すると確実にモジュールと関数の集合体と引数の二つの部分に分割することが可能なことに気づくだろう
そう,最初のスペースで分割すれば大まかな二つの部分に分割が可能だ
よって次のようになる
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
である.つまり,モジュール内にモジュールは定義されるが,関数内にモジュールは定義されない.
よって.
で区切った文字列のうち一番最後が関数であり,それ以外はモジュールである.
つまり.
で区切ったのち,最後とそれ以前を分けることができればよい.
次のようになる
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
関数の形式が次のようになる
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}
定義どおりに実装できていることが確認できる
今回は文字列からモジュールと関数を導き出した.これはまだ始まったばかりだ.
次回からは引数部分をリストに変換するが現時点で厳しい道のりなのは明白である.
以下ソースコード(マスターでゴリゴリ開発してるので悪しからず.)
GitHub: ElixirInterpreter