diff --git a/lib/pure.ex b/lib/pure.ex
index 99c4a90..6ded7e1 100644
--- a/lib/pure.ex
+++ b/lib/pure.ex
@@ -50,6 +50,19 @@ defmodule Pure do
end
@doc """
+ Checks if string is a match expression.
+
+ ## Examples
+
+ iex> Pure.string_is_match?("{a, b} = File.read(:stdin)", __ENV__)
+ true
+ """
+ def string_is_match?(str, env) when is_binary(str) do
+ {:ok, quoted} = Code.string_to_quoted(str)
+ is_match?(quoted, env)
+ end
+
+ @doc """
Checks if quoted expression is a match expression.
## Examples
@@ -62,20 +75,28 @@ defmodule Pure do
{erl, _, _, _env} = :elixir.quoted_to_erl(quoted, env)
IO.inspect(erl, limit: :infinity)
case erl do
- {:match, _, _, _} -> true
+ {:match, _, a, b} ->
+ a_is_pattern? = :pure.is_pattern_expr(a)
+ b_is_guard? = :erl_lint.is_guard_test(b)
+ IO.inspect([a: a, is_pattern?: a_is_pattern?])
+ IO.inspect([b: b, is_guard?: b_is_guard?])
+ a_is_pattern? && b_is_guard?
_ -> false
end
end
defmacro eval(str) when is_binary(str) do
- q = Code.string_to_quoted(str)
- quote do eval(unquote(q)) end
+ {:ok, q} = Code.string_to_quoted(str)
+ quote do
+ require Pure
+ Pure.eval(unquote(q))
+ end
end
defmacro eval(q) do
- if is_guard?(q, __ENV__) do
+ if is_guard?(q, __ENV__) || is_match?(q, __ENV__) do
q
else
- raise ArgumentError, "not a valid guard expression: #{Macro.to_string(q)}"
+ raise ArgumentError, "not a pure expression: #{Macro.to_string(q)}"
end
end
end
diff --git a/src/pure.erl b/src/pure.erl
new file mode 100644
index 0000000..b4f3399
--- /dev/null
+++ b/src/pure.erl
@@ -0,0 +1,38 @@
+-module(pure).
+
+-export([is_pattern_expr/1]).
+
+-import(lists, [all/2]).
+
+is_pattern_expr(Expr) ->
+ case is_pattern_expr_1(Expr) of
+ false -> false;
+ true ->
+ %% Expression is syntactically correct - make sure that it
+ %% also can be evaluated.
+ case erl_eval:partial_eval(Expr) of
+ {integer,_,_} -> true;
+ {char,_,_} -> true;
+ {float,_,_} -> true;
+ {atom,_,_} -> true;
+ {var,_,_} -> true;
+ {tuple,_,_} -> true;
+ X -> io:write(X), false
+ end
+ end.
+
+is_pattern_expr_1({char,_Anno,_C}) -> true;
+is_pattern_expr_1({integer,_Anno,_I}) -> true;
+is_pattern_expr_1({float,_Anno,_F}) -> true;
+is_pattern_expr_1({atom,_Anno,_A}) -> true;
+is_pattern_expr_1({tuple,_Anno,Es}) ->
+ all(fun is_pattern_expr/1, Es);
+is_pattern_expr_1({var,_Anno,_V}) -> true;
+is_pattern_expr_1({nil,_Anno}) -> true;
+is_pattern_expr_1({cons,_Anno,H,T}) ->
+ is_pattern_expr_1(H) andalso is_pattern_expr_1(T);
+is_pattern_expr_1({op,_Anno,Op,A}) ->
+ erl_internal:arith_op(Op, 1) andalso is_pattern_expr_1(A);
+is_pattern_expr_1({op,_Anno,Op,A1,A2}) ->
+ erl_internal:arith_op(Op, 2) andalso all(fun is_pattern_expr/1, [A1,A2]);
+is_pattern_expr_1(_Other) -> false.