Last active
September 17, 2016 04:38
-
-
Save christhekeele/bc9badf1bad500dc3f88e6e490bca39a to your computer and use it in GitHub Desktop.
Check quoted expressions for validity inside guards.
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 Guard do | |
@guardables ~W[ | |
== != === !== > >= < <= | |
and or not | |
+ - * / | |
<> | |
in | |
is_atom is_binary is_bitstring is_boolean | |
is_float is_function is_integer is_list | |
is_map is_nil is_number is_pid is_port | |
is_reference is_tuple | |
abs binary_part bit_size byte_size | |
div elem hd length map_size node | |
rem round self tl trunc tuple_size | |
]a | |
@doc """ | |
Validates that the given expressions are valid inside guards. | |
It returns `:ok` if the expression is a valid guard expression. Otherwise it | |
returns a tuple in the form of `{:error, remainder}` where `remainder` is the | |
invalid part of the quoted expression. | |
""" | |
@spec validate(Macro.t) :: :ok | {:error, term} | |
def validate(expr) do | |
find_invalid(expr) || :ok | |
end | |
# This is the magic clause | |
defp find_invalid({left, meta, right}) | |
when is_atom(left) and not left in @guardables and is_list(meta) and not is_atom(right), do: | |
{:error, {left, meta, right}} | |
# The rest is just the implementation of `Macro.validate` verbatim | |
defp find_invalid({left, right}), do: | |
find_invalid(left) || find_invalid(right) | |
defp find_invalid({left, meta, right}) when is_list(meta) and (is_atom(right) or is_list(right)), do: | |
find_invalid(left) || find_invalid(right) | |
defp find_invalid(list) when is_list(list), do: | |
Enum.find_value(list, &find_invalid/1) | |
defp find_invalid(pid) when is_pid(pid), do: nil | |
defp find_invalid(atom) when is_atom(atom), do: nil | |
defp find_invalid(num) when is_number(num), do: nil | |
defp find_invalid(bin) when is_binary(bin), do: nil | |
defp find_invalid(fun) when is_function(fun) do | |
unless :erlang.fun_info(fun, :env) == {:env, []} and | |
:erlang.fun_info(fun, :type) == {:type, :external} do | |
{:error, fun} | |
end | |
end | |
defp find_invalid(other), do: {:error, other} | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment