# `Feel.Runtime.RangeLiteral`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/runtime/range_literal.ex#L1)

Builds the value of a range literal such as `[1..10)` (DMN 1.6, grammar rule 66, section 10.3.2.7;
decision D-61).

The endpoints are expressions, so what they are is only known when the literal is run. The range is
a `Feel.Value.Range` when the endpoints that are not `null` are of one kind that has an order, and
that kind is a number, a string, a date, a time, a date and time, or a duration of one of the two
kinds. Anything else is `null` with a type mismatch: `[1.."a"]`, `[1..true]`, and `[[1]..[2]]` have
no value.

A `null` endpoint is an endpoint that is not there (`Feel.Value.Range`): `[1..null)` has a start and
no end, and its side is never included, whatever its bracket says. So `[1..null]` and `[1..null)` are
the same range. The order of the endpoints is not checked: `[5..1]` is a range that nothing is in.

The endpoints are not compared with each other here, and so a range of date and times with an offset and
one without is built; whether such a value is in the range is a question for the comparison.

# `build`

```elixir
@spec build(Feel.Value.t(), Feel.Value.t(), boolean(), boolean()) ::
  Feel.Runtime.Env.outcome()
```

Builds the range between two endpoint values, with the inclusion that the brackets say.

    iex> Feel.Runtime.RangeLiteral.build(Decimal.new(1), Decimal.new(10), true, false)
    {:ok, %Feel.Value.Range{start: Decimal.new(1), end: Decimal.new(10), start_included: true, end_included: false}}
    iex> Feel.Runtime.RangeLiteral.build(Decimal.new(1), nil, true, true)
    {:ok, %Feel.Value.Range{start: Decimal.new(1), end: nil, start_included: true, end_included: false}}
    iex> {:error, diagnostic} = Feel.Runtime.RangeLiteral.build(Decimal.new(1), "a", true, true)
    iex> diagnostic.message
    "the endpoints of a range cannot be a number and a string"

# `build_prefix`

```elixir
@spec build_prefix(:lt | :lte | :gt | :gte | :eq | :neq, Feel.Value.t()) ::
  Feel.Runtime.Env.outcome()
```

Builds the range that a prefix test says, as a value: `(< 10)`, `(>= x)`, `(= 10)` (Table 42, D-61).

The endpoint has to be of a kind that has an order, as the endpoints of an interval do, or be `null`, which is no
endpoint. The range has the properties of Table 42, and remembers the comparison it was written as
(`Feel.Value.Range`), so that it is not equal to the interval with the same ends:

| The test  | `start included`, `start` | `end`, `end included` |
| --------- | ------------------------- | --------------------- |
| `< 10`    | `false`, undefined        | `10`, `false`         |
| `<= 10`   | `false`, undefined        | `10`, `true`          |
| `> 10`    | `false`, `10`             | undefined, `false`    |
| `>= 10`   | `true`, `10`              | undefined, `false`    |
| `= 10`    | `true`, `10`              | `10`, `true`          |
| `!= 10`   | `false`, `10`             | `10`, `false`         |

The last is a decision (D-61): the endpoint on both sides and in neither, as the TCK's `0074#range_012` had it, which
the TCK has since commented out (dmn-tck/tck PR 671). `(!= 10)` is an equal range to another `(!= 10)` (`0068#range_012`)
and a value is in it when it is not equal to the endpoint.

    iex> Feel.Runtime.RangeLiteral.build_prefix(:lt, Decimal.new(10))
    {:ok, %Feel.Value.Range{start: nil, end: Decimal.new(10), start_included: false, end_included: false, operator: :lt}}
    iex> Feel.Runtime.RangeLiteral.build_prefix(:gte, Decimal.new(10))
    {:ok, %Feel.Value.Range{start: Decimal.new(10), end: nil, start_included: true, end_included: false, operator: :gte}}
    iex> {:ok, range} = Feel.Runtime.RangeLiteral.build_prefix(:lt, nil)
    iex> {range.start, range.end, range.operator}
    {nil, nil, :lt}
    iex> {:error, diagnostic} = Feel.Runtime.RangeLiteral.build_prefix(:eq, [Decimal.new(1)])
    iex> diagnostic.message
    "the endpoints of a range cannot be a list"

---

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