Skip to content

Instantly share code, notes, and snippets.

@mmmries
Last active August 29, 2015 14:17
Show Gist options
  • Save mmmries/b657c77845b07ee8cd34 to your computer and use it in GitHub Desktop.
Save mmmries/b657c77845b07ee8cd34 to your computer and use it in GitHub Desktop.
Elixir Term Parser
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
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