Packages

A simple expression language for elixir

Current section

Files

Jump to
ex_sel lib ex_sel.ex
Raw

lib/ex_sel.ex

defmodule ExSel do
@moduledoc """
Simple runtime expression language for elixir.
## Variables
A variable should start with a lowercase letter and is followed by mixed case letters, numbers or underscore
`true` and `false` are reserved symbols
# valid:
a , myVar , my_Var, var1
# invalid:
Aaaa , true, false, _myVar, my-var, 9var
## Arithmetic expressions:
The supported operators: `+` `-` `/` `*`
Additionally grouping is supported to override normal precedence rules.
# examples:
(1 + 2) * 5 / 4 - 2
var1 * 9 - varB
## Boolean expressions:
The supported comparison operators: `>` `>=` `<` `<=` `==` `!=`
The supported logic operators: `&&` `||` `!`
The supported boolean consts: `true` `false`
Additionally grouping is supported to override normal precedence rules.
# examples:
true
varA > (1 + 2) || varB == true
var1 * 9 - varB == varC
"""
@doc """
Parses the given arithmetic expression into an ExSel.Ast
## Example
iex> ExSel.aexpr("8 + 2 / 4")
{:ok, {:+, [8, {:/, [2, 4]}]}}
"""
@spec aexpr(binary) :: {:ok, ExSel.Ast.t()} | {:error, term}
def aexpr(expression) do
expression
|> prep_input
|> ExSel.Parser.aexpr()
|> unwrap_res
end
@doc """
Parses the given boolean expression into an ExSel.Ast
## Example
iex> ExSel.bexpr("8 + 2 > 4")
{:ok, {:>, [{:+, [8, 2]}, 4]}}
"""
@spec bexpr(binary) :: {:ok, ExSel.Ast.t()} | {:error, term}
def bexpr(expression) do
expression
|> prep_input
|> ExSel.Parser.bexpr()
|> unwrap_res
end
defp prep_input(input), do: String.trim(input)
defp unwrap_res(result) do
case result do
{:ok, [acc], "", _, _line, _offset} ->
{:ok, acc}
{:ok, _, rest, _, _line, _offset} ->
{:error, "could not parse: " <> rest}
{:error, reason, _rest, _context, _line, _offset} ->
{:error, reason}
end
end
@doc """
Evaluates the expression ast.
First build an ast using `aexpr/1` or `bexpr/1`.
To specify variables that should be available during evaluation pass a map with their values.
## Example
iex> {:ok, ast} = ExSel.aexpr("varA + varB / 4")
iex> my_vars = %{"varA" => 8, "varB" => 2}
iex> ExSel.eval!(ast, my_vars)
8.5
"""
@spec eval!(ExSel.Ast.t(), ExSel.Ast.eval_ctx()) :: ExSel.Ast.eval_result() | no_return
def eval!(ast, variables \\ %{}), do: ExSel.Ast.eval!(ast, variables)
end