Packages
expression
2.5.1
2.49.1
2.49.0
2.48.0
2.47.5
2.47.4
2.47.3
2.47.2
2.47.1
2.47.0
2.46.0
2.45.1
2.45.0
2.44.1
2.44.0
2.43.0
2.42.1
2.41.4
2.41.3
2.41.2
2.41.0
2.40.0
2.38.0
2.37.0
2.36.2
2.36.1
2.36.0
2.35.0
2.34.0
2.33.1
2.33.0
2.32.1
2.31.6
2.31.3
2.31.0
2.30.0
2.28.0
2.27.3
2.27.2
2.27.1
2.27.0
2.26.1
2.26.0
2.25.0
2.24.2
2.24.1
2.24.0
2.23.9
2.23.8
2.23.7
2.23.6
2.23.4
2.23.3
2.23.2
2.23.1
2.23.0
2.22.2
2.22.0
2.21.0
2.20.0
2.19.0
2.18.0
2.17.0
2.16.0
2.14.0
2.13.0
2.12.0
2.11.0
2.10.1
2.10.0
2.9.0
2.8.0
2.6.0
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.1
2.4.0
2.3.2
2.3.1
2.3.0
2.2.1
2.2.0
2.1.1
2.1.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.1
1.1.0
1.0.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.0
0.4.3
0.4.2
0.4.1
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.1.0
A Excel like expression parser, compatible with FLOIP Expression language.
Current section
Files
Jump to
Current section
Files
lib/expression/callbacks.ex
defmodule Expression.Callbacks do
@moduledoc """
The function callbacks for the standard function set available
in FLOIP expressions.
This should be relatively swappable with another implementation.
The only requirement is the `handle/3` function.
FLOIP functions are case insensitive. All functions in this callback
module are implemented as lowercase names.
Some functions accept a variable amount of arguments. Elixir doesn't
support variable arguments in functions.
If a function accepts a variable number of arguments the convention
is to call the `<function_name>_vargs/2` callback where the context
is given as the first argument and the argument list as a second
argument.
Reserved names such as `and`, `if`, and `or` are suffixed with an
underscore.
"""
alias Expression.Callbacks.Standard
@reserved_words ~w[and if or not]
@doc """
Convert a string function name into an atom meant to handle
that function
Reserved words such as `and`, `if`, and `or` are automatically suffixed
with an `_` underscore.
"""
def atom_function_name(function_name) when function_name in @reserved_words,
do: atom_function_name("#{function_name}_")
def atom_function_name(function_name) do
String.to_atom(function_name)
end
@doc """
Handle a function call while evaluating the AST.
Handlers in this module are either:
1. The function name as is
2. The function name with an underscore suffix if the function name is a reserved word
3. The function name suffixed with `_vargs` if the takes a variable set of arguments
"""
@spec handle(module :: module, function_name :: binary, arguments :: [any], context :: map) ::
{:ok, any} | {:error, :not_implemented}
def handle(module \\ Standard, function_name, arguments, context) do
case implements(module, function_name, arguments) do
{:exact, module, function_name, _arity} ->
{:ok, apply(module, function_name, [context] ++ arguments)}
{:vargs, module, function_name, _arity} ->
{:ok, apply(module, function_name, [context, arguments])}
{:error, reason} ->
{:error, reason}
end
end
def implements(module \\ Standard, function_name, arguments) do
exact_function_name = atom_function_name(function_name)
vargs_function_name = atom_function_name("#{function_name}_vargs")
Code.ensure_loaded!(Standard)
cond do
# Check if the exact function signature has been implemented
function_exported?(module, exact_function_name, length(arguments) + 1) ->
{:exact, module, exact_function_name, length(arguments) + 1}
# Check if it's been implemented to accept a variable amount of arguments
function_exported?(module, vargs_function_name, 2) ->
{:vargs, module, vargs_function_name, 2}
# Check if the exact function signature has been implemented
function_exported?(Standard, exact_function_name, length(arguments) + 1) ->
{:exact, Standard, exact_function_name, length(arguments) + 1}
# Check if it's been implemented to accept a variable amount of arguments
function_exported?(Standard, vargs_function_name, 2) ->
{:vargs, Standard, vargs_function_name, 2}
# Otherwise fail
true ->
{:error, "#{function_name} is not implemented."}
end
end
defmacro __using__(_opts) do
quote do
import Expression.Callbacks.EvalHelpers
defdelegate handle(module \\ __MODULE__, function_name, arguments, context),
to: Expression.Callbacks
end
end
end