# `Expression.V2`

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](https://hexdocs.pm/elixir/typespecs.html#basic-types) either double or single quoted.
* [integers](https://hexdocs.pm/elixir/typespecs.html#basic-types) such as `1`, `2`, `40`, `55`
* [floats](https://hexdocs.pm/elixir/typespecs.html#basic-types) such as `3.141592653589793`
* [booleans](https://hexdocs.pm/elixir/typespecs.html#basic-types) which can be written in any mixed case such as `tRue` or `TRUE`, `False` etc
* `Range.t` such as `1..10`, also with steps `1..10//2`
* `Date.t` such as `2022-01-01` which is parsed into `~D[2022-01-01]`
* `Time.t` such as `10:30` which is parsed into `~T[10:30:00]`
* ISO formatted `DateTime.t` such as `2022-05-24T00:00:00` which is parsed into `~U[2022-05-24 00:00:00.0Z]`
* US formatted `DateTime.t` such as `01-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]
    ]

# `compile`

```elixir
@spec compile(expression :: String.t()) :: [term()]
```

# `compile_block`

# `debug`

```elixir
@spec debug(String.t() | [term()]) :: String.t()
```

Return the code generated for the Abstract Syntax tree or
Expression string provided.

# `default_value`

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`

```elixir
@spec escape(String.t()) :: String.t()
```

# `eval`

```elixir
@spec eval(expression :: String.t(), context :: Expression.V2.Context.t()) :: [term()]
```

Evaluate a string with expressions against a given context

# `eval_as_string`

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

```elixir
@spec eval_ast([term()], context :: Expression.V2.Context.t()) :: [term()]
```

Evaluate a parsed Expression against a given context

# `eval_block`

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

```elixir
@spec eval_block_ast([term()], context :: Expression.V2.Context.t()) :: [term()]
```

Evaluate the given AST against a given context

# `parse`

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

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

```elixir
@spec read_attribute(map() | list(), binary() | integer()) :: term()
```

This function is referenced by `Expression.V2.Compile` to
make access to values in Maps or Lists easier

# `stringify`

```elixir
@spec stringify(term()) :: String.t()
```

---

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