Feel.Runtime.RangeLiteral (feel v0.1.0)

Copy Markdown View Source

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.

Summary

Functions

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

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

Functions

build(start, finish, start_included, end_included)

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(operator, endpoint)

@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 teststart included, startend, end included
< 10false, undefined10, false
<= 10false, undefined10, true
> 10false, 10undefined, false
>= 10true, 10undefined, false
= 10true, 1010, true
!= 10false, 1010, 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"