Expression.V2.Parser (expression v2.48.0)
Copy MarkdownA NimbleParsec parser for FLOIP expressions.
FLOIP Expressions consist of plain text and of blocks. Plain text is returned untouched but blocks are evaluated.
Blocks are prefixed with an @ sign. Blocks can either have expressions between brackets or
be used in a shorthand form when wanting to use a single function or variable substitution.
As an example, the following are identical:
@(now())and@now()@contact.nameand@(contact.name)
However, a full expression needs to be within brackets:
Tomorrow's is @(today().day + 1)
This parses it into an Abstract Syntax Tree (AST) which follows a style much like a Lisp would.
It parses expressions in Infix notation such as
1 + 1 and parses it into lists where the operator is the first element and the second element
is the list of arguments for the operator.
["+", [1, 1]]Functions are expressed as:
{"function name", [arg1, arg2]}Until we have a fixed scope of allowed functions, or if we can dynamically look up whether an
atom is a function or a variable reference, we will need to rely on tuples to represent functions
as otherwise the system has no means to distinguish the following AST as being a function call
or a list as a variable:
["echo", [1, 2, 3]]Without being able to say ahead of time whether or not echo/1 is a known function, the
system cannot reliable determine whether the result of this AST should be ["echo", [1, 2, 3]]
or the result of echo(1, 2, 3).
Variable references are single values.
"contact"This module provides two functions for parsing. parse/2 which will parse a full FLOIP expression
including text and blocks, and expression/2 which will parse expression blocks.
Internally parse/2 refers to the same parsers as expression/2 for things that are expressions.
Summary
Functions
@spec expression(binary(), keyword()) :: {:ok, [term()], rest, context, line, byte_offset} | {:error, reason, rest, context, line, byte_offset} when line: {pos_integer(), byte_offset}, byte_offset: non_neg_integer(), rest: binary(), reason: String.t(), context: map()
Parse a block and return the AST
Example
iex> Expression.V2.Parser.expression("contact.age + 1")
{:ok, [{"+", [{"__property__", ["contact", "age"]}, 1]}], "", %{}, {1, 0}, 15}
@spec parse(binary(), keyword()) :: {:ok, [term()], rest, context, line, byte_offset} | {:error, reason, rest, context, line, byte_offset} when line: {pos_integer(), byte_offset}, byte_offset: non_neg_integer(), rest: binary(), reason: String.t(), context: map()
Parse an expression and return the AST
Example
iex> Expression.V2.Parser.parse("hello @world the time is @now()")
{:ok, ["hello ", ["world"], " the time is ", [{"now", []}]], "", %{}, {1, 0}, 31}