cond.pony

class Cond[
  S: (Any #read & Equatable[S]),
  D: Any #share = None,
  V: Any #share = None]
  is RuleNodeWithBody[S, D, V]

  let _body: RuleNode[S, D, V]
  let _cond: {(Success[S, D, V]): (Bool, (String | None))} val

  new create(
    body': RuleNode[S, D, V],
    cond': {(Success[S, D, V]): (Bool, (String | None))} val)
  =>
    _body = body'
    _cond = cond'

  fun action(): (Action[S, D, V] | None) =>
    None

  fun body(): (this->(RuleNode[S, D, V]) | None) =>
    _body

  fun call(depth: USize, loc: Loc[S]): _RuleFrame[S, D, V] =>
    _CondFrame[S, D, V](this, depth, loc, _body, _cond)

class _CondFrame[S: (Any #read & Equatable[S]), D: Any #share, V: Any #share]
  is _Frame[S, D, V]

  let _rule: RuleNode[S, D, V] box
  let _depth: USize
  let _loc: Loc[S]
  let _body: RuleNode[S, D, V] box
  let _cond: {(Success[S, D, V]): (Bool, (String | None))} val

  new create(
    rule: RuleNode[S, D, V] box,
    depth: USize,
    loc: Loc[S],
    body: RuleNode[S, D, V] box,
    cond: {(Success[S, D, V]): (Bool, (String | None))} val)
  =>
    _rule = rule
    _depth = depth
    _loc = loc
    _body = body
    _cond = cond

  fun ref run(child_result: (Result[S, D, V] | None)): _FrameResult[S, D, V] =>
    match child_result
    | let success: Success[S, D, V] =>
      (let succeeded, let message) = _cond(success)
      if succeeded then
        _Dbg() and _Dbg.out(_depth, "= " + success.string())
        success
      else
        _Dbg() and _Dbg.out(_depth, "= failure: " + message.string())
        Failure[S, D, V](_rule, success.start, message)
      end
    | let failure: Failure[S, D, V] =>
      _Dbg() and _Dbg.out(_depth, "= " + failure.string())
      failure
    else
      _Dbg() and _Dbg.out(_depth, "COND @" + _loc.string())
      _body.call(_depth + 1, _loc)
    end