Expression.V2 (expression v2.26.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]
]
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 parsed Expression against a given context
Evaluate a string with an expression block against a context
Evaluate the given AST against a given 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
Functions
compile(expression)
compile_block(final)
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(expression :: 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_ast(parsed_parts, context \\ Context.new())
@spec eval_ast([term()], context :: Expression.V2.Context.t()) :: [term()]
Evaluate a parsed Expression against a given context
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_block_ast(ast, context)
@spec eval_block_ast([term()], context :: Expression.V2.Context.t()) :: [term()]
Evaluate the given AST against a given 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