Expression.V2 (expression v2.19.0)
A second attempt at the parser, hopefully a little easier to read & maintain.
parse/1
parsed an Expression into AST.
eval/3
evaluates the given AST using the context supplied.
For details on how this is done please read Expression.V2.Parser
and
Expression.V2.Compile
.
This parser & evaluator supports the following:
- strings either double or single quoted.
- integers such as
1
,2
,40
,55
- floats such as
3.141592653589793
- booleans which can be written in any mixed case such as
tRue
orTRUE
,False
etc Range.t
such as1..10
, also with steps1..10//2
Date.t
such as2022-01-01
which is parsed into~D[2022-01-01]
Time.t
such as10:30
which is parsed into~T[10:30:00]
- ISO formatted
DateTime.t
such as2022-05-24T00:00:00
which is parsed into~U[2022-05-24 00:00:00.0Z]
- US formatted
DateTime.t
such as01-02-2020 23:23:23
which is parsed into~U[2020-02-01T23:23:23Z]
- Lists of any of the above, such as
[1, 2, 3]
or[1, 1.234, "john"]
- Reading properties off of nested objects such as maps with a full stop, such as
contact.name
returning"Doe"
from%{"contact" => %{"name" => "Doe"}}
- Reading attributes off of maps, such as
contact[the_key]
which returns"Doe"
from%{"contact" => %{"name" => "Doe"}, "the_key" => "name"}
- Anonymous functions with
&
and&1
as capture operators,&(&1 + 1)
is an anonymous function that increments the input by 1.
The result of a call to eval/3
is a list of typed evaluated items. It is up to the integrating library to determine how
best to convert these into a final end user representation.
Examples
iex> alias Expression.V2
iex> V2.eval("the date is @date(2022, 2, 20)")
["the date is ", ~D[2022-02-20]]
iex> V2.eval("the answer is @true")
["the answer is ", true]
iex> V2.eval("22 divided by 7 is @(22 / 7)")
["22 divided by 7 is ", 3.142857142857143]
iex> V2.eval(
...> "Hello @proper(contact.name)! Looking forward to meet you @date(2023, 2, 20)",
...> V2.Context.new(%{"contact" => %{"name" => "mary"}})
...> )
["Hello ", "Mary", "! Looking forward to meet you ", ~D[2023-02-20]]
iex> V2.eval("@map(1..3, &date(2023, 1, &1))")
[[~D[2023-01-01], ~D[2023-01-02], ~D[2023-01-03]]]
iex> V2.eval(
...> "Here is the multiplication table of @number: @(map(1..10, &(&1 * number)))",
...> V2.Context.new(%{"number" => 5})
...> )
[
"Here is the multiplication table of ",
5,
": ",
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
]
Link to this section Summary
Functions
Return the code generated for the Abstract Syntax tree or Expression string provided.
Return the default value for a potentially complex value.
Evaluate a string with expressions against a given context
Evaluate an expression and cast all items to strings before joining the full result into a single string value to be returned.
Evaluate a string with an expression block against a context
Evaluate the results returned by the compile step against the context
Parse a string with expressions into AST for the compile step
Parse a string with an expression block into AST for the compile step
This function is referenced by Expression.V2.Compile
to
make access to values in Maps or Lists easier
Link to this section Functions
compile(expression)
compile_block(list)
debug(expression)
Return the code generated for the Abstract Syntax tree or Expression string provided.
default_value(val, context \\ nil)
Return the default value for a potentially complex value.
Complex values can be Maps that have a __value__
key, if that's
returned then we can to use the __value__
value when eval'ing against
operators or functions.
escape(expression)
eval(expression, context \\ Context.new())
@spec eval(String.t(), context :: Expression.V2.Context.t()) :: [term()]
Evaluate a string with expressions against a given context
eval_as_string(expression, context \\ Context.new())
@spec eval_as_string(String.t(), Expression.V2.Context.t()) :: String.t()
Evaluate an expression and cast all items to strings before joining the full result into a single string value to be returned.
This calls eval/2
internally, maps the results with default_value/2
followed by stringify/1
and then joins them.
eval_block(expression_block, context \\ Context.new())
@spec eval_block(String.t(), context :: Expression.V2.Context.t()) :: term() | {:error, reason :: String.t(), bad_parts :: String.t()}
Evaluate a string with an expression block against a context
eval_in_context(list, context)
@spec eval_in_context( literal :: [term()] | term() | function(), Expression.V2.Context.t() ) :: [term()]
Evaluate the results returned by the compile step against the context
parse(expression)
@spec parse(String.t()) :: {:ok, [term()]} | {:error, reason :: String.t(), bad_parts :: String.t()}
Parse a string with expressions into AST for the compile step
parse_block(expression_block)
@spec parse_block(String.t()) :: {:ok, [term()]} | {:error, reason :: String.t(), bad_parts :: String.t()}
Parse a string with an expression block into AST for the compile step
read_attribute(map, item)
This function is referenced by Expression.V2.Compile
to
make access to values in Maps or Lists easier