success.pony

type Result[
  S: (Any #read & Equatable[S]),
  D: Any #share = None,
  V: Any #share = None]
  is (Success[S, D, V] | Failure[S, D, V])
  """
  The result of a parse attempt, either successful or failed.
  """

class box Success[
  S: (Any #read & Equatable[S]),
  D: Any #share = None,
  V: Any #share = None]
  """
  The result of a successful parse.
  """

  let node: RuleNode[S, D, V] box
  """The rule that matched successfully."""

  let start: Loc[S]
  """The location at which the rule matched."""

  let next: Loc[S]
  """The location one past the end of the match."""

  let children: ReadSeq[Success[S, D, V]]
  """Results from child rules' matches."""

  new create(
    node': RuleNode[S, D, V] box,
    start': Loc[S],
    next': Loc[S],
    children': ReadSeq[Success[S, D, V]] box = [])
  =>
    node = node'
    start = start'
    next = next'
    children = children'

  fun _values(data: D)
    : ReadSeq[V] val
  =>
    """
    Call the matched rules' actions to assemble a custom result value.
    """
    match _values_aux(data, 0)
    | (let result_values: Array[V] val, _) =>
      result_values
    else
      []
    end

  fun _values_aux(data: D, indent: USize)
    : ((Array[V] val | None), (Bindings[S, D, V] | None))
  =>
    // collect values from child results
    ( var result_values: (Array[V] trn | None)
    , var bindings: (Bindings[S, D, V] | None) ) =
      match children.size()
      | 0 =>
        (None, None)
      else
        // fold child results
        var result_values': (Array[V] trn | None) = None
        var bindings': (Bindings[S, D, V] | None) = None
        for child in children.values() do
          (let crv, let cb) = child._values_aux(data, indent + 1)

          match crv
          | let crv': Array[V] val if crv'.size() > 0 =>
            result_values' =
              match consume result_values'
              | let rv: Array[V] trn =>
                rv.append(crv')
                consume rv
              else
                let rv: Array[V] trn = Array[V]
                rv.append(crv')
                consume rv
              end
          end

          match cb
          | let child_bindings': Bindings[S, D, V] =>
            match bindings'
            | let parent_bindings: Bindings[S, D, V] =>
              // we want shallower bindings to override deeper ones
              // and later ones to override earlier ones
              for (v, child_binding) in child_bindings'.pairs() do
                match try parent_bindings(v)? end
                | let parent_binding: Binding[S, D, V] box =>
                  if child_binding.depth <= parent_binding.depth then
                    parent_bindings.update(v, child_binding)
                  end
                else
                  parent_bindings.update(v, child_binding)
                end
              end
            else
              bindings' = Bindings[S, D, V] .> concat(child_bindings'.pairs())
            end
          end
        end
        (consume result_values', bindings')
      end

    // run node's action
    var val_values: (Array[V] val | None) = consume result_values
    match node.action()
    | let action: Action[S, D, V] =>
      let val_values': Array[V] val =
        match val_values
        | let rv': Array[V] val =>
          consume rv'
        else
          []
        end
      let bindings' =
        match bindings
        | let bb': Bindings[S, D, V] =>
          bb'
        else
          Bindings[S, D, V]
        end
      let value = action(data, this, val_values', bindings')
      match value
      | let value': V =>
        val_values = [ value' ]
      else
        val_values = None
      end
    end

    // bind variable if necessary (even with empty values!)
    match node
    | let bind: Bind[S, D, V] box =>
      let bound_values: Array[V] val =
        match val_values
        | let val_values': Array[V] val =>
          val_values'
        else
          []
        end
      match bindings
      | let bindings': Bindings[S, D, V] =>
        bindings'.update(
          bind.variable, Binding[S, D, V](this, indent, bound_values))
      else
        bindings = Bindings[S, D, V] .> update(
          bind.variable, Binding[S, D, V](this, indent, bound_values))
      end
    end

    (val_values, bindings)

  fun _indent(n: USize): String =>
    recover
      var n' = n * 2
      let s = String(n')
      while (n' = n' - 1) > 0 do
        s.push(' ')
      end
      s
    end

  fun eq(that: box->Success[S, D, V]): Bool =>
    (this.node is that.node)
      and (this.start == that.start)
      and (this.next == that.next)

  fun string(): String iso^ =>
    match node
    | let rule: NamedRule[S, D, V] box =>
      "Success(" + rule.name + "@[" + start.string() + "," + next.string() +
        "))"
    else
      "Success(@[" + start.string() + "," + next.string() + "))"
    end