1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-10 12:47:45 +03:00

Add read/print functionality up to quote (step 1)

This commit is contained in:
ekmartin 2015-09-03 02:07:29 +02:00
parent 51c2c1fea7
commit b5aff35fe8
4 changed files with 112 additions and 0 deletions

13
elixir/lib/mal/printer.ex Normal file
View File

@ -0,0 +1,13 @@
defmodule Mal.Printer do
def print_str(mal) when is_atom(mal), do: Atom.to_string(mal)
def print_str(mal) when is_integer(mal), do: Integer.to_string(mal)
def print_str(mal) when is_bitstring(mal), do: mal
def print_str({ :symbol, value }), do: value
def print_str(mal) when is_list(mal) do
output = mal
|> Enum.map(fn(x) -> print_str(x) end)
|> Enum.join(" ")
"(#{output})"
end
end

58
elixir/lib/mal/reader.ex Normal file
View File

@ -0,0 +1,58 @@
# TODO: def -> defp for everything but read_str
defmodule Mal.Reader do
import Mal.Types
def read_str(input) do
output = tokenize(input)
|> read_form
|> elem(0)
{:ok, output}
catch
{:invalid, message} -> {:error, message}
end
def tokenize(input) do
regex = ~r/[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/
Regex.scan(regex, input, capture: :all_but_first)
|> List.flatten
|> List.delete_at(-1) # Remove the last match, which is an empty string
end
def read_form([next | rest] = tokens) do
case next do
"(" <> _ ->
read_list(tokens)
_ ->
token = read_atom(next)
{token, rest}
end
end
def read_list([_ | tokens]), do: do_read_list(tokens, [])
defp do_read_list([], acc), do: throw({:invalid, "expected ')', got EOF"})
defp do_read_list([head | tail] = tokens, acc) do
case head do
")" <> _ -> {Enum.reverse(acc), tail}
_ ->
{token, rest} = read_form(tokens)
do_read_list(rest, [token | acc])
end
end
def read_atom("nil"), do: nil
def read_atom("true"), do: true
def read_atom("false"), do: false
def read_atom(":" <> rest), do: String.to_atom(rest)
def read_atom(token) do
cond do
String.starts_with?(token, "\"") -> token
String.starts_with?(token, "'") -> token
integer?(token) ->
Integer.parse(token)
|> elem(0)
true -> {:symbol, token}
end
end
end

9
elixir/lib/mal/types.ex Normal file
View File

@ -0,0 +1,9 @@
defmodule Mal.Types do
def integer?(input) do
Regex.match?(~r/^-?[0-9]+$/, input)
end
def float?(input) do
Regex.match?(~r/^-?[0-9][0-9.]*$/, input)
end
end

View File

@ -0,0 +1,32 @@
defmodule Mix.Tasks.Step1ReadPrint do
def run(_), do: main
def main do
IO.write(:stdio, "user> ")
IO.read(:stdio, :line)
|> read_eval_print
main
end
def read(input) do
Mal.Reader.read_str(input)
end
def eval({:ok, input}), do: {:ok, input}
def eval({:error, message}), do: {:error, message}
def print({:ok, output}) do
IO.puts(Mal.Printer.print_str(output))
end
def print({:error, message}) do
IO.puts(message)
end
def read_eval_print(:eof), do: exit(0)
def read_eval_print(line) do
read(line)
|> eval
|> print
end
end