# `Expression.V2.Parser`

A 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.name` and `@(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](https://en.wikipedia.org/wiki/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.

# `expression`

```elixir
@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}

# `parse`

```elixir
@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}

# `to_double_quoted_string`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
