# `Predicator.Visitors.InstructionsVisitor`
[🔗](https://github.com/riddler/predicator-ex/blob/v9.4.1/lib/predicator/visitors/instructions_visitor.ex#L1)

Visitor that converts AST nodes to stack machine instructions.

This visitor implements post-order traversal to generate instruction lists
that can be executed by the stack-based evaluator. Instructions are generated
in the correct order for stack-based evaluation.

## Source positions

Internally the traversal pairs every instruction with the source position of
the AST node that emitted it. `visit/2` discards those positions and returns
the plain instruction list; `visit_with_positions/2` returns both. The
instruction list is identical either way - positions never enter the
instruction format itself, so cross-language interchange and stored compiled
artifacts are unaffected (ADR-0001). The paired value is whatever the node
carried in its trailing slot, so an AST parsed with `spans: true` yields a
span table rather than a position table.

Both entry points require a node with a trailing slot. A caller hand-building
an AST supplies `nil` there, which produces no entry in the position table.

## Examples

    iex> ast = {:literal, 42, nil}
    iex> Predicator.Visitors.InstructionsVisitor.visit(ast, [])
    [["lit", 42]]

    iex> ast = {:identifier, "limit", nil}
    iex> Predicator.Visitors.InstructionsVisitor.visit(ast, [])
    [["load", "limit"]]

    iex> ast = {:comparison, :gt, {:identifier, "limit", nil}, {:literal, 85, nil}, nil}
    iex> Predicator.Visitors.InstructionsVisitor.visit(ast, [])
    [["load", "limit"], ["lit", 85], ["compare", "GT"]]

    iex> ast = {:logical_and, {:literal, true, nil}, {:literal, false, nil}, nil}
    iex> Predicator.Visitors.InstructionsVisitor.visit(ast, [])
    [["lit", true], ["jump_if_falsy_or_pop", 2], ["lit", false]]

    iex> ast = {:function_call, "len", [{:identifier, "name", nil}], nil}
    iex> Predicator.Visitors.InstructionsVisitor.visit(ast, [])
    [["load", "name"], ["call", "len", 1]]

    iex> ast = {:if, {:literal, true, nil}, {:block, [], nil}, nil, nil}
    iex> Predicator.Visitors.InstructionsVisitor.visit(ast, [])
    [["lit", true], ["pop_jump_if_falsy", 1]]

# `annotated`

```elixir
@type annotated() ::
  {[binary() | term()],
   Predicator.Types.position() | Predicator.Types.span() | nil}
  | {[binary() | term()],
     Predicator.Types.position() | Predicator.Types.span() | nil,
     [Predicator.Types.position() | Predicator.Types.span() | nil]}
```

One instruction paired with the source position of the node that emitted it.

Every clause but one produces the two-element form. A `["store", n]`
instruction produces the three-element form instead, whose third element is
`location_segment_annotations/1`'s list - one annotation per lhs segment,
root-first. Nothing else in this module ever emits or reads a third element.

# `visit`

```elixir
@spec visit(
  Predicator.Parser.visitable(),
  keyword()
) :: [[binary() | term()]]
```

Visits an AST node and returns stack machine instructions.

Uses post-order traversal to ensure operands are pushed onto the stack
before operators are applied.

## Parameters

- `ast_node` - The AST node to convert to instructions
- `opts` - Optional visitor options (currently unused)

## Returns

List of instructions in the format `[["operation", ...args]]`.

# `visit_with_positions`

```elixir
@spec visit_with_positions(
  Predicator.Parser.visitable(),
  keyword()
) ::
  {[[binary() | term()]],
   Predicator.Types.position_table() | Predicator.Types.span_table()}
```

Returns the instruction list and a side table mapping each instruction's
0-based index to the source position of the AST node that emitted it.

Nodes carrying a `nil` position contribute no entry, so a position-free AST
yields an empty table. The instruction list is identical to `visit/2`'s.

## Examples

    iex> ast = {:comparison, :gt, {:identifier, "limit", {1, 1}}, {:literal, 85, {1, 9}}, {1, 7}}
    iex> Predicator.Visitors.InstructionsVisitor.visit_with_positions(ast)
    {[["load", "limit"], ["lit", 85], ["compare", "GT"]],
     %{0 => {1, 1}, 1 => {1, 9}, 2 => {1, 7}}}

    iex> Predicator.Visitors.InstructionsVisitor.visit_with_positions({:literal, 42, nil})
    {[["lit", 42]], %{}}

# `visit_with_segment_positions`

```elixir
@spec visit_with_segment_positions(
  Predicator.Parser.visitable(),
  keyword()
) ::
  {[[binary() | term()]],
   Predicator.Types.position_table() | Predicator.Types.span_table(),
   Predicator.Types.segment_position_table()}
```

Returns the instruction list, the position table `visit_with_positions/2`
returns, and a per-store segment-position table.

The segment table maps each `["store", n]` instruction's 0-based index to
the list `location_segment_annotations/1` built for its lhs chain - one
annotation per location segment, root-first, whatever `n` counts as depth
(`location_depth/1`). Every other instruction contributes no entry. The
instruction list and the position table are identical to what
`visit_with_positions/2` returns for the same AST - this function differs
only in returning the third table alongside them.

## Examples

    iex> {:ok, program} = Predicator.parse_program("a.b = 1", spans: false)
    iex> Predicator.Visitors.InstructionsVisitor.visit_with_segment_positions(program)
    {[["lit", "a"], ["lit", "b"], ["lit", 1], ["store", 2]],
     %{0 => {1, 1}, 1 => {1, 3}, 2 => {1, 7}, 3 => {1, 1}},
     %{3 => [{1, 1}, {1, 3}]}}

---

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