Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse Elixir Terms From A String

Tags:

elixir

Erlang has a facility for parsing Erlang terms from a string. This has the advantage of avoiding dynamic code execution (ie not trusting user input), but allows the user to enter any valid literal values.

See: http://www.erlang.org/faq/how_do_i.html#idp32770608

Does Elixir allow for something similar to this? Can I easily accept a string and attempt to parse it?

Ideally it would look something like:

str = "{:foo, %{bar: true}, nil}"
Elixir.parse_term(str) # => {:foo, %{bar: true}, nil}
like image 997
mmmries Avatar asked Mar 24 '15 05:03

mmmries


1 Answers

With Elixir pattern matching this is easy peasy:

defmodule ParseTerm do
  def parse(str) when is_binary(str)do
    case str |> Code.string_to_quoted do
      {:ok, terms} -> {:ok, _parse(terms)}
      {:error, _}  -> {:invalid_terms}
    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({_term_type, _place, terms}), do: terms # to ignore functions and operators
end

Use it like this:

iex(1)> ParseTerm.parse "%{1=>:a, :q=>:z, :w => [1,2, 1.618, {1,2, %{a: \"1.2\" } }] }"
{:ok, %{1 => :a, :q => :z, :w => [1, 2, 1.618, {1, 2, %{a: "1.2"}}]}}
like image 81
X4lldux Avatar answered Nov 08 '22 01:11

X4lldux