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

The built-in functions of FEEL (DMN 1.6, section 10.3.4).

This module holds their names, and finds and calls the ones that are written. The parser needs
the names: many are several words (`string length`, `date and time`, `years and months
duration`), and which tokens form a name depends on the names in scope, so the built-ins are part of
the outermost scope (`Feel.Analysis.ScopeStack`).

The names are canonical (see `Feel.Name`).

## How a function is called (D-56)

A function is a `Feel.Builtin.Function` that its area module lists with `functions/0`. A name that is
in `names/0` and in no list is not written yet (`implemented?/1`), and the analyzer says so.

The analyzer turns a call of a function into the IR node `{:call, meta, {:builtin, meta, name},
arguments}` (`Feel.Analysis.Calls`), with the arguments put in the order of the parameters: a call with
named arguments is for the first signature that has them all (of those, the first that has them in the order
they are written, D-79), and a parameter it leaves out is `null`.
Both backends evaluate the arguments, in order, and then `call/3` runs the function on the values and
gives an outcome, which they settle at the span of the call. That is the whole contract, and generated
code makes the same call. A function whose result can be larger than its arguments (`string join`, `replace`,
`flatten`...) works out the size it is going to make and, when the limits refuse it, ends the run as `settle/3`
would for the finished value (`Feel.Builtin.Size`): the call does not return.

A call whose arguments no signature accepts (too many or too few, or a name no parameter has) is a
`null` with an `:arity_mismatch` error, at run time; the analyzer sees it and warns, as it does for an
operation that can only be a type mismatch.

## Areas

The functions themselves are grouped by area of the specification, one module
each, listed by `areas/0`. Every name belongs to exactly one of them, and a test
keeps their lists and this module's list equal (D-56).

| Module                    | Section      | Functions |
| ------------------------- | ------------ | --------- |
| `Feel.Builtin.Conversion` | 10.3.4.1     | 8         |
| `Feel.Builtin.Boolean`    | 10.3.4.2     | 1         |
| `Feel.Builtin.String`     | 10.3.4.3     | 13        |
| `Feel.Builtin.List`       | 10.3.4.4, .9 | 24        |
| `Feel.Builtin.Number`     | 10.3.4.5     | 14        |
| `Feel.Builtin.Temporal`   | 10.3.4.6, .8, .11 | 7    |
| `Feel.Builtin.Range`      | 10.3.4.7     | 14        |
| `Feel.Builtin.Context`    | 10.3.4.10    | 5         |

Generated code may call them, so they are part of the ABI of `Feel.Runtime`.

# `areas`

```elixir
@spec areas() :: [module()]
```

Returns the modules that hold the built-in functions, one per area of the
specification. Each has a `names/0` with the names of its functions.

    iex> Feel.Builtin.String in Feel.Builtin.areas()
    true

# `arity_mismatch`

```elixir
@spec arity_mismatch(Feel.Builtin.Function.t(), non_neg_integer()) ::
  Feel.Diagnostic.t()
```

The diagnostic for a call of a function with a number of arguments that none of its signatures takes.

    iex> {:ok, function} = Feel.Builtin.function("date")
    iex> Feel.Builtin.arity_mismatch(function, 2).message
    "date takes 1 or 3 arguments, not 2"

# `builtin?`

```elixir
@spec builtin?(Feel.Name.t()) :: boolean()
```

Returns `true` when `name` is the canonical name of a built-in function.

    iex> Feel.Builtin.builtin?("date and time")
    true
    iex> Feel.Builtin.builtin?("price")
    false

# `call`

```elixir
@spec call(
  Feel.Runtime.Env.t(),
  Feel.Name.t(),
  [Feel.Value.t()],
  Feel.Builtin.Function.signature() | nil
) :: Feel.Runtime.Env.outcome()
```

Calls a function on the values of its arguments, in the environment of a run, and returns the outcome
(`t:Feel.Runtime.Env.outcome/0`) for the caller to settle.

A function that is refused for the size of its result, or whose regular expression passes its budget, does not
return: it ends the run (`Feel.Runtime.Env.run/2` is where that is caught). A number of arguments that no signature
accepts is `{:error, diagnostic}` with the code `:arity_mismatch`. A function that is not written raises
`RuntimeError`, as the unwritten parts of the runtime do, and a name that is no built-in's is an `:unknown_function`
error. The function is found with the extensions of the environment (`function/2`).

A call by name has chosen a signature (`Feel.Analysis.Calls.arrange/2`), and the function is given the values and
not that signature, so with `signature` the values are checked against it first (D-56): one that its parameter
does not take makes the result `null` with an `:argument_out_of_domain` error, and the function is not called.
Without one the function decides, by the kinds of the values, among the signatures it has.

    iex> env = Feel.Runtime.Env.new()
    iex> Feel.Builtin.call(env, "date", ["2012-12-25"])
    {:ok, ~D[2012-12-25]}
    iex> {:error, diagnostic} = Feel.Builtin.call(env, "date", [])
    iex> diagnostic.code
    :arity_mismatch
    iex> {:ok, put} = Feel.Builtin.function("context put")
    iex> [key_signature, _keys_signature] = put.signatures
    iex> arguments = [%{"y" => %{"a" => Decimal.new(0)}}, ["y", "a"], Decimal.new(2)]
    iex> {:ok, _context} = Feel.Builtin.call(env, "context put", arguments)
    iex> {:error, diagnostic} = Feel.Builtin.call(env, "context put", arguments, key_signature)
    iex> diagnostic.code
    :argument_out_of_domain

# `call_at`

```elixir
@spec call_at(
  Feel.Runtime.Env.t(),
  Feel.Name.t(),
  [Feel.Value.t()],
  Feel.Builtin.Function.signature() | nil,
  Feel.Diagnostic.span() | nil
) :: {Feel.Value.t(), Feel.Runtime.Env.t()}
```

`call/4` settled at the span of the call, as the backends make it (`Feel.Runtime.Env.settle/3`), with the implicit
conversions of the arguments reported first (D-46): a list of one element given where the parameter takes the
element, and a value given where it takes a list, are each an `:info` `:implicit_conversion` at the span.

A call whose arguments are taken as they are, or by no signature (that is the error of the call), reports nothing,
and neither does one that is `null` with an error: the signatures say what the specification converts, and a function
that does not (`date([2020], 1, 2)`) ends with its error, which says it did not. `call/4` does not report, so that a
caller who wants the outcome of the function alone has it.

    iex> env = Feel.Runtime.Env.new()
    iex> {value, env} = Feel.Builtin.call_at(env, "contains", [["foobar"], "of"], nil, {0, 30})
    iex> {value, Enum.map(Feel.Runtime.Env.diagnostics(env), &{&1.severity, &1.code, &1.span})}
    {false, [{:info, :implicit_conversion, {0, 30}}]}
    iex> {value, env} = Feel.Builtin.call_at(Feel.Runtime.Env.new(), "contains", ["foobar", "of"], nil, {0, 30})
    iex> {value, Feel.Runtime.Env.diagnostics(env)}
    {false, []}

# `clear_cache`

```elixir
@spec clear_cache() :: :ok
```

Forgets the table of functions that `function/1` keeps (D-56), so that the next call builds it again from the areas.

The table is built from `functions/0` of each area on the first call and kept for the life of the VM, since an area makes
its list of functions anew at each call (a lookup took 1.5 to 3 µs, and every call of a function in a run made one). A
VM in which the code of an area is loaded again (a shell that recompiles, a test that does) calls this after it.

    iex> :ok = Feel.Builtin.clear_cache()
    iex> {:ok, function} = Feel.Builtin.function("abs")
    iex> function.name
    "abs"

# `function`

```elixir
@spec function(Feel.Name.t()) :: {:ok, Feel.Builtin.Function.t()} | :error
```

Finds a built-in function that is written, by its canonical name.

    iex> {:ok, function} = Feel.Builtin.function("date and time")
    iex> function.name
    "date and time"
    iex> Feel.Builtin.function("price")
    :error

# `function`

```elixir
@spec function(Feel.Name.t(), Feel.Extensions.t()) ::
  {:ok, Feel.Builtin.Function.t()} | :error
```

Finds a function with the extensions that are on (`Feel.Extension`, D-81): a function of DMN 1.6 with the
overloads that the extensions add to it (`Feel.Builtin.Function.with_overloads/2`), or a function that an
extension adds. A function of DMN 1.6 that is not written is not found, whatever the extensions overload.

    iex> {:ok, number} = Feel.Builtin.function("number", Feel.Extensions.none())
    iex> Feel.Builtin.Function.arities(number)
    [1, 3]
    iex> Feel.Builtin.function("shout", Feel.Extensions.none())
    :error

# `implemented?`

```elixir
@spec implemented?(Feel.Name.t()) :: boolean()
```

Returns `true` when the function is written. All 86 names are (D-87 wrote the last, `sort`), so it is
`false` only for a name that is no built-in's; the path for a name that is registered and not
written is kept for the day one is.

    iex> Feel.Builtin.implemented?("now")
    true
    iex> Feel.Builtin.implemented?("sort")
    true
    iex> Feel.Builtin.implemented?("price")
    false

# `names`

```elixir
@spec names() :: [Feel.Name.t()]
```

Returns the canonical names of the built-in functions, sorted.

    iex> "string length" in Feel.Builtin.names()
    true

# `value`

```elixir
@spec value(Feel.Name.t(), Feel.Extensions.t()) ::
  {:ok, Feel.Value.Function.t()} | :error
```

A built-in function as a value (D-82): `abs` where an expression names it and does not call it. It is a
`Feel.Value.Function` of the kind `:builtin` with the signatures of the function (and of the overloads that the
extensions add to it), which a call finds the function by its name from. The type of a parameter or of the result
is its type if the signature takes only one, and `Any` if it takes more. The signatures that differ only in types are
one signature here, as they are for a call by name (`Feel.Builtin.Function.merge_variants/1`): `abs` is
`function<Any> -> Any`, though a call of it is typed by the kind of its argument. The signatures that were made one are kept
beside it, each as it was (D-171): what a call of the value does for a number, or for a duration, is what that signature says
(`Feel.Value.Function.refinements/1`). They are not function types of the value, which is an instance of the function types
of the merged signature, as the specification has it.

    iex> {:ok, length} = Feel.Builtin.value("string length")
    iex> {length.kind, length.name, Feel.Value.Function.type(length)}
    {:builtin, "string length", {:function, [:string], :number}}
    iex> {:ok, abs} = Feel.Builtin.value("abs")
    iex> Feel.Value.Function.type(abs)
    {:function, [:any], :any}
    iex> Feel.Value.Function.refinements(abs) |> length()
    3
    iex> Feel.Builtin.value("price")
    :error

---

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