ElixirでElixirインタプリタを作成していく(第３回 文字列のリストを値として変換していく)

#ElixirでElixirインタプリタを作成していく
##第3回 文字列のリストを値として変換していく
前回、一つの文字列として与えられた引数を引数ごとに文字列のリストとして返す関数を作成した
たとえば

```elixir
"1, 2.0, \"hello\"" |> function
# ["1", "2.0", "\"hello\""]
```
と分割される
これを

```elixir
[ 1, 2.0, "hello"]
```
と変換したい

###定義
関数名:`arg_convertion`
引数:`[String]`
返り値:`List`

###実装
個人的に本来はガード節を用いて関数を分けることを行いたかったが
ガード節の仕様上無理だということがわかったため今回は`cond`を用いて実装する
はじめに関数の宣言部を以下に示す

```elixir
def arg_convertion(list = [head | tail]) do
end
```

今から型の推論部分を実装していくがここでは正規表現を用いて実装していく、また`List`や`Map`等の方を組み合わせた型は省いたものを実装していく

####Integer
整数型であるIntegerは正義表現を用いて次のように判別が可能だ

```elixir
Regex.match?(~r/\d/, head)
```
この条件がマッチしたときの文字列を`String.to_integer`関数を用いてintegerに変換する

```elixir
Regex.match?(~r/\d/, head) ->
   num =
      head
      |> String.to_integer()

  [num | arg_convertion(tail)]
```

これを同じようにすればFloatでも可能そうだ
####Float
Floatは小数が表現可能な型なので小数点を目印にすれば良さそうである

```elixir
Regex.match?(~r/\d\.\d/, head) ->
   num =
      head
      |> String.to_float()

   [num | arg_convertion(tail)]
```
ただし、このコードを用いる際は注意が必要である
それはこのコードをIntegerよりも前に実装しては行けない点である
`Regex.match?`はマッチする部分がある時点で`true`を返すので
Integerにマッチしてしまうからである

####String

```elixir
Regex.match?(~r/\".*\"/, head)
```
これを用いることで認識可能である
この時点で`head`には

```erlang
"\"string\""
```
となっていることがわかるだろうか
そこで最初と最後の`"`を削除することを考える
そこで次の関数を定義する

```elixir:Core.utils.ex
defmodule ElixirInterpreter.Core.Utils do
  ...
  def remove_head_last(str, head, last) do
    len = String.length(str)

    str
    |> String.slice(head, len)
    |> String.reverse()
    |> String.slice(last, len)
    |> String.reverse()
  end
end
```
この関数は文字列を引数に持ちその`head`と`last`の文字数分を削除する関数となっている
これよりも良い関数がありそうなのでその際は知らせていただけると助かります

また次の事柄に気づくだろうか（ここが一番考えた気がする）
例えば

```erlang
"\"\""
```
の場合、おそらくコーダーが意図したものは`""`という文字列だろう
しかし、実際は異なる実際にはこれは文字列そのままとして認識されるため`\"\"`となっている
そこで次の関数を使用することとする

```elixir:Core.utils.ex
  def input_convertion(str) when is_binary(str), do: str |> String.replace("\\", "")
```
(この関数は文字として存在するエスケープ文字を認識させるだけなので本来の意味のエスケープ文字を認識させることはできません)

この２つを仕様して文字列認識は次のように記述できる

```elixir
Regex.match?(~r/\".*\"/, head) ->
  str =
    head
    |> Core.Utils.remove_head_last(1, 1)
    |> Core.Utils.input_convertion()

  [str | arg_convertion(tail)]
```

####nil
型ではないがこれは例外としてこの時点で実装していく

```elixir
Regex.match?(~r/nil/, head) ->
  [nil | arg_convertion(tail)]
```

####Atom
申し訳ないこればかりはコードを見ていただく他ない
Atomは`:atom`と`:"atom"`のような２つの場合がある
このときの分割は

```elixir
":atom"     |> [":atom"]
":\"atom\"" |> [":" , "atom"]
```
となってしまいコードが汚くなってしまっている
またここでは正規表現を用いず最初の文字が`:`であることを条件としている
それはキーワードリスト等と混同しないようにするためである

```elixir
String.first(head) == ":" ->
  [ head | tail] = case String.length(head) do
    1 ->
      [head | tail] = tail
      head = head |> Core.Utils.remove_head_last(1, 1)
      [head | tail]
    _ ->
      head = head |> Core.Utils.remove_head_last(1, 0)
      [ head | tail]
  end
  atom = head |> Core.Utils.input_convertion |> String.to_atom()
  [ atom | Core.arg_convertion(tail)]
```

最後がゴリ押しの終わり方になったのは許してほしい
分割を見直す等してさらなる改善を目標にしていく
次回はリスト等のデータ構造による型を実装していく


第0回　[目次](https://qiita.com/Sepi110902/items/10e5f0645cacfc322bd4#_reference-08b7184ee12141f375b2)
第2回　[基本的な引数の分割](https://qiita.com/Sepi110902/items/63a302ecf52afbf5737a)
第4回　データ構造による型を実装する(作成次第リンク)

以下ソースコード(マスターでゴリゴリ開発してるので悪しからず．）
GitHub: [ElixirInterpreter](https://github.com/SeitaHigashi/elixir_interpreter)