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

The part of analyzing a call of a built-in function that does not need the analyzer (D-56).

`Feel.Analysis.Analyzer` analyzes the arguments, which takes the whole analysis, and asks this
module the rest:

  * `callee/3` says what the callee of a call is: a built-in function that is written, one that
    is not, or a value (D-82): a name that the scope or a context around has, which shadows the built-in of the
    same name (D-26), a name that is nothing, or any other expression.
  * `arrange/2` puts the arguments in the order of the parameters, which is the form both backends
    take (`Feel.Builtin`). Positional arguments are in order already, and their number has to be one
    that a signature accepts. Named arguments are for the first signature that has a parameter of each
    name (with the signatures that differ from it only in types, which the names do not tell apart), and a
    parameter they leave out is `null`: `time(hour: 1, minute: 2, second: 3)` is
    `time(1, 2, 3, null)`, and `is(value1: x)` is `is(x, null)` (the TCK, 0103). Two arguments of one
    name, and a name no signature has, are errors, as is a wrong number of arguments.
  * `mismatch/3` says whether the arguments, as the analyzer typed them, are ones that no signature that fits
    takes (D-56): `context put(context: c, key: ["y", "a"], value: 2)` has a list of two for a `key` that is a
    string. Only a certain mismatch is one, so an argument that may be `null` or of any type is not. Nor do the
    signatures take arguments that have to be of one kind and cannot be (D-155): `before("a", 2)`, or `max(d, 2)`
    for a date `d` that is not `null`.
  * `result_type/3` is the type of the result: what the signatures that the arguments may be for can give
    (`abs` has one for each kind it takes, and `abs(d)` of a duration is a duration), and `null` unless the
    function never gives it.

An error is not an analysis error: the call is a `null` with that error when it runs, as the TCK has it
(`day of year(value: @"1970-01-01")` is `null`, with an error), and the analyzer warns.
Type mismatches keep the ordinary runtime call boundary, which checks actual argument values
and constructs its diagnostic independently of the analysis tier. Structural errors such as
invalid arity carry a fixed diagnostic on the call instead.

# `argument`

```elixir
@type argument() :: Feel.AST.expression() | :missing
```

An argument in the order of the parameters: an expression, or `:missing` for a parameter the call leaves out.

# `arrange`

```elixir
@spec arrange(Feel.Builtin.Function.t(), Feel.AST.arguments()) ::
  {:ok, [argument()], [Feel.Builtin.Function.signature()]}
  | {:error, Feel.Diagnostic.t()}
```

Puts the arguments of a call in the order of the parameters of the function, with the signatures that the
call fits. `{:error, diagnostic}` if it fits none.

    iex> {:ok, function} = Feel.Builtin.function("time")
    iex> {:ok, ast} = Feel.parse("time(minute: 2, hour: 1, second: 3)")
    iex> {:call, _meta, _callee, arguments} = ast
    iex> {:ok, arranged, _signatures} = Feel.Analysis.Calls.arrange(function, arguments)
    iex> Enum.map(arranged, fn :missing -> :missing; {:literal, _, value} -> Decimal.to_integer(value) end)
    [1, 2, 3, :missing]

# `callee`

```elixir
@spec callee(
  Feel.AST.expression(),
  Feel.Analysis.ScopeStack.t() | nil,
  Feel.Extensions.t()
) ::
  {:builtin, Feel.Builtin.Function.t()} | {:unwritten, Feel.Name.t()} | :value
```

What the callee of a call is.

    iex> {:ok, ast} = Feel.parse(~s|date("2020-01-01")|)
    iex> {:call, _meta, callee, _arguments} = ast
    iex> {:builtin, function} = Feel.Analysis.Calls.callee(callee, nil)
    iex> function.name
    "date"

# `mismatch`

```elixir
@spec mismatch(Feel.Builtin.Function.t(), [Feel.Builtin.Function.signature()], [
  Feel.IR.t()
]) ::
  :ok | {:error, Feel.Diagnostic.t()}
```

Whether the arguments of a call, analyzed and in the order of the parameters, are ones that none of the
signatures it fits takes (D-56): the call is then `null` with an `:argument_out_of_domain` error, which is what
the function would say when it runs, but a function is given the values and not the signature that the names of
a call chose, so one that reads a list for a `key` as `keys` does not (`context put`, TCK 1146 `nested008`).

Only a **certain** mismatch is one. An argument that may be `null` or may be of any type is not, and the
conversions of section 10.3.2.9.4 are allowed: a list of one element is that element where a value is wanted,
and a value is the list of it where a list is wanted, and a date is a date and time where one is wanted (D-56). The length of a list is the one of the node that is a list
literal or a constant (D-193), and unknown for any other. A parameter that the registry types `any` takes every
argument, as does a signature the call is not the only one for.

A signature that says some arguments have to be of one kind (`same_kind`, D-155) rejects arguments whose kinds have
nothing in common, though each is taken by its parameter: a string and a number are two points, and `before` takes no
pair of them. The kinds an argument may be of are those of the members of its type that have an order (a number, a
string, a date, a time, a date and time, a duration of either kind); `null`, `Any` and the name of a model type
may be of every kind, as they are taken by every parameter, so one of them is never in disagreement. Nor is a
list that may have no element, whose elements are not compared when it has none (`max([])` is `null`, with no
error); the items of a list literal or a constant are each one that the call has, and are compared. A range is
not looked at: its type holds the range with no endpoint, which can be related to a point of any kind.

    iex> const = fn value -> {:const, %{span: {0, 0}, type: Feel.Type.Union.of_value(value)}, value} end
    iex> {:ok, function} = Feel.Builtin.function("context put")
    iex> [key_signature | _] = function.signatures
    iex> arguments = [const.(%{"x" => Decimal.new(1)}), const.(["y", "a"]), const.(Decimal.new(2))]
    iex> {:error, diagnostic} = Feel.Analysis.Calls.mismatch(function, [key_signature], arguments)
    iex> diagnostic.message
    "context put is not defined for context, list, number"
    iex> arguments = [const.(%{"x" => Decimal.new(1)}), const.(["y"]), const.(Decimal.new(2))]
    iex> Feel.Analysis.Calls.mismatch(function, [key_signature], arguments)
    :ok
    iex> {:ok, before} = Feel.Builtin.function("before")
    iex> {:error, diagnostic} = Feel.Analysis.Calls.mismatch(before, before.signatures, [const.("a"), const.(Decimal.new(2))])
    iex> diagnostic.message
    "before is not defined for string, number"
    iex> Feel.Analysis.Calls.mismatch(before, before.signatures, [const.("a"), const.("b")])
    :ok

# `quiet_places`

```elixir
@spec quiet_places([Feel.Builtin.Function.signature()], non_neg_integer()) :: [
  non_neg_integer()
]
```

The places (from 0) of the arguments of a call that are read quietly (D-100): those where every signature that the call fits
has a quiet parameter (`Feel.Builtin.Function.parameter/3`).

    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> Feel.Analysis.Calls.quiet_places(abs.signatures, 1)
    []

# `result_type`

```elixir
@spec result_type(
  Feel.Builtin.Function.t(),
  [Feel.Builtin.Function.signature()],
  [Feel.IR.t()] | nil
) ::
  Feel.Type.Union.t()
```

The type of the result of a call: the types that the signatures give which the arguments may be for, and `null` too
unless the function is never `null`.

A signature that the arguments are certainly not for (`mismatch/3` says it of all of them for a call that is `null`
with its error) is not one that the call can be answered by, so `abs(duration("P1D"))` is a duration, though `abs` has
signatures for numbers and for the other kind of duration. Only a certain mismatch leaves a signature out: an argument
that may be of any type is for every signature, and so is any argument of a function that does not check its arguments
(`Feel.Builtin.Function`). A `null` that an argument may be is not for every signature as it is in `mismatch/3`, since
what the call gives for a `null` is `null`, which its type has anyway, and the rest of the argument says which signature
gives the other results: `abs(x)` of a number that may be `null` is a number that may be `null`, and not a duration.
A parameter that takes `null` among its types takes it, and an argument that is only `null` is for every signature.
With no argument that tells them apart, the result is that of all the signatures. Without the list of the arguments at
all (`nil`, the default) the signatures alone are asked about: the types they declare for the result, and not those
that follow the arguments (`result_from`), and no rule of a family (`Feel.Analysis.Images`) is asked. A call with no
argument (`now()`, `concatenate()`) is the empty list, and a rule may answer it.

    iex> {:ok, function} = Feel.Builtin.function("date")
    iex> signatures = Feel.Builtin.Function.with_arity(function, 1)
    iex> Feel.Analysis.Calls.result_type(function, signatures)
    [:date, :null]
    iex> {:ok, is} = Feel.Builtin.function("is")
    iex> Feel.Analysis.Calls.result_type(is, is.signatures)
    [:boolean]
    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> Feel.Analysis.Calls.result_type(abs, abs.signatures)
    [:number, :years_and_months_duration, :days_and_time_duration, :null]
    iex> duration = {:var, %{span: {0, 0}, type: [:days_and_time_duration]}, "d"}
    iex> abs |> Feel.Analysis.Calls.result_type(abs.signatures, [duration]) |> Feel.Type.Union.format()
    "days and time duration in [PT0S..max]"
    iex> maybe_number = {:var, %{span: {0, 0}, type: [:number, :null]}, "x"}
    iex> abs |> Feel.Analysis.Calls.result_type(abs.signatures, [maybe_number]) |> Feel.Type.Union.format()
    "number in [0..max] | Null"

# `single`

```elixir
@spec single([Feel.Builtin.Function.signature()]) ::
  Feel.Builtin.Function.signature() | nil
```

The one signature that a call by name has chosen, if these are its signatures: those that differ only in types are
one (`Feel.Builtin.Function.merge_variants/1`), so `abs(n: x)` has the signature of `n` that takes the kinds of
all three. `nil` if there are others, or none.

    iex> {:ok, abs} = Feel.Builtin.function("abs")
    iex> {:ok, _arranged, signatures} = Feel.Analysis.Calls.arrange(abs, {:named, [{["n"], {:literal, %{span: {0, 0}}, Decimal.new(1)}}]})
    iex> length(signatures)
    3
    iex> Enum.map(Feel.Analysis.Calls.single(signatures).parameters, & &1.types)
    [[:number, :days_and_time_duration, :years_and_months_duration]]
    iex> Feel.Analysis.Calls.single([])
    nil

# `written`

```elixir
@spec written(Feel.AST.arguments()) :: [Feel.AST.expression()]
```

The expressions of the arguments as they are written, for a call that has to be `null` with its error:
they are analyzed all the same, so that their errors are reported.

---

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