Last active
August 29, 2015 14:17
-
-
Save mmmries/b657c77845b07ee8cd34 to your computer and use it in GitHub Desktop.
Elixir Term Parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule TermParser do | |
def parse(str) when is_binary(str)do | |
case str |> Code.string_to_quoted do | |
{:ok, terms} -> hydrate_terms(terms) | |
{:error, err} -> {:error, err} | |
end | |
end | |
defp hydrate_terms(terms) do | |
try do | |
{:ok, _parse(terms)} | |
rescue | |
e in ArgumentError -> {:error, e} | |
end | |
end | |
# atomic terms | |
defp _parse(term) when is_atom(term), do: term | |
defp _parse(term) when is_integer(term), do: term | |
defp _parse(term) when is_float(term), do: term | |
defp _parse(term) when is_binary(term), do: term | |
defp _parse([]), do: [] | |
defp _parse([h|t]), do: [_parse(h) | _parse(t)] | |
defp _parse({a, b}), do: {_parse(a), _parse(b)} | |
defp _parse({:"{}", _place, terms}) do | |
terms | |
|> Enum.map(&_parse/1) | |
|> List.to_tuple | |
end | |
defp _parse({:"%{}", _place, terms}) do | |
for {k, v} <- terms, into: %{}, do: {_parse(k), _parse(v)} | |
end | |
defp _parse(_) do | |
raise ArgumentError, message: "string contains non-literal term(s)" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule TermParserTest do | |
use ExUnit.Case | |
import Showoff.TermParser | |
test "parses a tuple with scalars" do | |
assert {:ok, {1,2,3}} == parse("{1,2,3}") | |
end | |
test "parses nested structures" do | |
assert {:ok, [{:circle, %{cx: 1, cy: 2, r: 3}, nil}]} == parse("[{:circle, %{cx: 1, cy: 2, r: 3}, nil}]") | |
end | |
test "it returns an error for invalid syntax" do | |
assert {:error, _} = parse("{14, ..") | |
end | |
test "it returns an error for executable code" do | |
assert {:error, _} = parse("File.read!(\"/etc/passwd\")") | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment