Feel.Runtime.Path (feel v0.1.0)

Copy Markdown View Source

Selecting from a value by a name: e.name (DMN 1.6, Tables 65 to 68 and section 10.3.2.5, D-65).

select/2 takes the value of e and the canonical name (Feel.Name) and returns an Feel.Runtime.Env.outcome/0. What it selects depends on the kind of the value:

The valuee.name
a contextthe entry of that key; null if the entry is null; an error if there is none
a lista list of the same length: the selection from each element (see below)
a dateyear, month, day, weekday, value
a timehour, minute, second, time offset, timezone, value
a date and timeall of those
a years and months durationyears, months, value
a days and time durationdays, hours, minutes, seconds, value
a rangestart, end, start included, end included
nullan error (null.b is null, flagged as an error: TCK 0057#010)
anything elsean error

A name that the kind does not have is an error with the code :invalid_property, and so is a key that a context does not have ({a: 1}.b, TCK 0057#009). So is a component of the other kind of duration (duration("P1Y").days): Table 67 says null for it, and the TCK flags an error (0074#ym_duration_003 to #006, dt_duration_001, #002), which is what the run reports (D-65). What Table 67 says is null and the TCK does not flag is not an error: time offset and timezone of a value that has none. The components of a duration carry its sign, and are the normalized ones (duration("P1DT25H").days is 2, and duration("-P1Y2M").months is -2, notes 05, E36).

A list

The selection from a list is the list of the selections from its elements (section 10.3.2.5): [{x: 1, y: 2}, {x: 2}].y is [2, null]. Inside a list a missing entry is null and not an error (TCK 0069#033, #034), and so is an element that has no such entry because it is no context, a date with no such property, null, or a list: there is no rule for a list of lists, and it is not looked into (notes 02, A-29). An empty list gives the empty list.

The descendant

e...name is the values that a context has under the key name, at any depth (section 10.3.2.5, Table 71): the context's own entry first, then those of the contexts nested in its entries, each in turn ({a: {b: {b: 1}}}...b is [{b: 1}, 1]). It is a list always, and [] when there is none (notes 05, E38). descend/3 reads it as the notes do, where the text is silent (D-65); with the environment of the run it also holds the list of a context to max_list_length as it makes it:

  • The order is a pre-order walk, with the entries of a context taken in the order they have (D-135).
  • Contexts nested in a list are not looked into; the text speaks of nested contexts. An entry whose value is a list is a value like any other, and is in the result if it is the key.
  • A list on the left is the list of the descendants of each element, as a path is ([c1, c2]...b is a list of two lists), with null for an element that is no context.
  • null, and a value that is not a context, are an error, as for a path.

value

The value of a duration is its months or its seconds. That of a date, a time and a date and time is the number of seconds that Feel.Runtime.Temporal.value/1 gives (D-68): from the epoch 1970-01-01T00:00:00Z, for a date and for a date and time with an offset or a zone id, and the seconds of the day less the offset for a time. A date and time with no zone has none, and is null with an error (:invalid_property).

iex> Feel.Runtime.Path.select(%{"a" => Decimal.new(1)}, "a")
{:ok, Decimal.new(1)}
iex> {:error, %Feel.Diagnostic{code: :invalid_property}} = Feel.Runtime.Path.select(%{"a" => 1}, "b")
iex> Feel.Runtime.Path.select([%{"y" => Decimal.new(2)}, %{"x" => Decimal.new(1)}], "y")
{:ok, [Decimal.new(2), nil]}
iex> Feel.Runtime.Path.select(~D[2018-12-10], "weekday")
{:ok, Decimal.new(1)}
iex> Feel.Runtime.Path.select(Feel.Value.DaysAndTimeDuration.new(93_784), "minutes")
{:ok, Decimal.new(3)}

Summary

Functions

Selects the values under a key from a context and from the contexts nested in it. See the module documentation.

Selects the entry or the property of a name from a value. See the module documentation.

Functions

descend(value, key, env \\ nil)

Selects the values under a key from a context and from the contexts nested in it. See the module documentation.

With the environment of the run, the descendants of a context are counted as they are found, and the walk ends at the first one over max_list_length, which is the error that a finished list gets (Feel.Runtime.Env.check_list_length/2). A context that is in a context twice is walked twice, so n levels of it are 2ⁿ descendants, and the list is made whole before the limit looks at it. The descendants of the contexts in a list are lists of their own, and are not looked at: no list but the one that a run gives is checked.

iex> inner = %{"b" => Decimal.new(1)}
iex> Feel.Runtime.Path.descend(%{"a" => %{"b" => inner}}, "b")
{:ok, [inner, Decimal.new(1)]}
iex> Feel.Runtime.Path.descend(%{"a" => 1}, "b")
{:ok, []}
iex> {:error, %Feel.Diagnostic{code: :invalid_property}} = Feel.Runtime.Path.descend(nil, "b")
iex> shared = %{"a" => %{"b" => 1}}
iex> env = Env.new(limits: Limits.new(max_list_length: 2))
iex> {:ok, _} = Feel.Runtime.Path.descend(%{"x" => shared, "y" => shared}, "b", env)
iex> {:error, %Error{kind: :limit}} = Feel.Runtime.Env.run(env, fn env ->
...>   {Feel.Runtime.Path.descend(%{"x" => shared, "y" => shared, "z" => shared}, "b", env), env}
...> end)

select(list, key)

Selects the entry or the property of a name from a value. See the module documentation.