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

The names visible at one point of an expression, innermost first (DMN 1.6, section
10.3.2.11).

Evaluating an expression looks a name up in a stack of contexts. The first context
that has the name wins, and nothing is merged. From the innermost out, the stack
holds:

  1. The special contexts, innermost construct first: `item` and the keys of the
     element in a filter predicate; the iterator variables and `partial` in a `for`;
     the variables of `some` and `every`; the parameters of a function being
     defined; `?` in a unary test.
  2. The local context, when the expression is the value of an entry of a context
     being built: the other entries of that context.
  3. The enclosing contexts of that one, inner to outer.
  4. The global context: the names the expression is given, which come from
     `Feel.Scope`.
  5. The built-in context, last. It is not stored here, since the built-in
     functions are a fixed table; a name that `resolve/2` does not find may be a
     built-in, and the analyzer checks that after the stack.

Because lookup goes in this order, any construct may reuse a name that exists
further out, including a built-in's, and shadows it inside its extent. `{sum: 1,
...}` makes `sum` a number there, and a call `sum(...)` in it then finds a value
that is not a function.

Names in a stack are canonical (see `Feel.Name`).

# `binding`

```elixir
@type binding() :: %{
  :kind =&gt; binding_kind(),
  :type =&gt; Feel.Type.t(),
  optional(:id) =&gt; pos_integer(),
  optional(:origin) =&gt; Feel.Analysis.Narrowing.origin()
}
```

A binding: its kind and type, and for a local the `id` that its reads carry in their metadata (`:local` nodes tell which binding
they are of) and, if it is bound to a condition, the `origin` of that condition (`t:Feel.Analysis.Narrowing.origin/0`).

# `binding_kind`

```elixir
@type binding_kind() :: :var | :local | :item | :item_field
```

What a name is bound to.

  * `:var` - a name from the bindings.
  * `:local` - a variable bound by a construct, such as an iterator variable, a
    parameter, or a context entry. The analyzer keeps the locals in a stack of their own, and
    their type is the inferred type of what they are bound to, a `Feel.Type.Union`.
  * `:item` - the element in a filter predicate.
  * `:item_field` - a key of that element, known to exist.

# `frame`

```elixir
@type frame() :: %{kind: frame_kind(), names: %{optional(Feel.Name.t()) =&gt; binding()}}
```

# `frame_kind`

```elixir
@type frame_kind() :: :special | :local | :enclosing | :global
```

# `t`

```elixir
@type t() :: %Feel.Analysis.ScopeStack{depth: non_neg_integer(), frames: [frame()]}
```

# `depth`

```elixir
@spec depth(t()) :: non_neg_integer()
```

Returns the number of contexts in the stack.

# `new`

```elixir
@spec new(Feel.Scope.t()) :: t()
```

Starts a stack with the names of a scope as its global context.

    iex> stack = Feel.Analysis.ScopeStack.new(Feel.Scope.put_name(%Feel.Scope{}, "age", :number))
    iex> Feel.Analysis.ScopeStack.resolve(stack, "age")
    {:ok, %{kind: :var, type: :number}, :global}

# `pop`

```elixir
@spec pop(t()) :: t()
```

Removes the innermost context. Raises when the stack is empty.

# `push`

```elixir
@spec push(t(), frame_kind(), %{optional(Feel.Name.t()) =&gt; binding()}) :: t()
```

Pushes a context on the inside of the stack.

    iex> stack = Feel.Analysis.ScopeStack.new(%Feel.Scope{})
    iex> stack = Feel.Analysis.ScopeStack.push(stack, :special, %{"item" => %{kind: :item, type: :number}})
    iex> Feel.Analysis.ScopeStack.resolve(stack, "item")
    {:ok, %{kind: :item, type: :number}, :special}

# `resolve`

```elixir
@spec resolve(t(), Feel.Name.t()) :: {:ok, binding(), frame_kind()} | :error
```

Looks a name up from the innermost context outwards.

Returns the binding and the kind of context it was found in, or `:error` when no
context has the name. The built-in functions are not searched; see the module
documentation.

    iex> stack = Feel.Analysis.ScopeStack.new(Feel.Scope.put_name(%Feel.Scope{}, "x", :number))
    iex> stack = Feel.Analysis.ScopeStack.push(stack, :special, %{"x" => %{kind: :local, type: :string}})
    iex> Feel.Analysis.ScopeStack.resolve(stack, "x")
    {:ok, %{kind: :local, type: :string}, :special}
    iex> Feel.Analysis.ScopeStack.resolve(stack, "y")
    :error

---

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