Capture < EmptyObject {

Patterns < EmptyObject {
  UnaryBase < Common::Patterns::UnaryBase { }

  NamedCapture      < UnaryBase {
    construct: Constructions::NamedCapture.new(
      inner: inner.construct
      captargs: [self.name]
    )
  }
  NamedTextCapture  < UnaryBase {
    construct: Constructions::NamedTextCapture.new(
      inner: inner.construct
      captargs: [self.name]
    )
  }
  NamedTokenCapture < UnaryBase {
    construct: Constructions::NamedTokenCapture.new(
      inner: inner.construct
      captargs: [self.name]
    )
  }
  Reduction         < UnaryBase { var block, var args: []
    construct: Constructions::Reduction.new(
      inner: inner.construct
      captargs: [self.code, *self.args]
    )
  }

  RuleCall          < UnaryBase { var name
    construct: Constructions::RuleCall.new(name: name)
  }
}

Constructions < EmptyObject {
  UnaryBase < Common::Constructions::UnaryBase { }

  NamedCapture      < UnaryBase { var captargs }
  NamedTextCapture  < UnaryBase { var captargs }
  NamedTokenCapture < UnaryBase { var captargs }
  Reduction         < UnaryBase { var captargs }

  RuleCall          < UnaryBase { var name }
}

Patterns << {
  ##
  # Reduction and associated named captures implementation

  Reduction << {
    Environment < EmptyObject, BasicDecorators {
      var captures # A Hash of capture names to values
      var builder # The fallback delegate for methods that are not capture names

      method_missing: |name, *args, &block|
        captures.fetch(name) { builder.__send__(name, *args, &block) }

      # Implement the action for the reduction in the '!' method.
      # It will have access to the captures via method_missing.
      "!": null
    }

    code: block.block.compiled_code
  }
}

Constructions << {
  UnaryBase << { const bytecode_can_capture?: true }

  NamedCapture << { bytecode: |g| {
    Rubinius::Type.object_respond_to?(inner, :inlaid) &? (
      inner.inlaid = &{ g.capture(:m_split, captargs) }
      g.capture(:m_start)
      inner.bytecode(g)
      g.capture(:m_end, captargs)
    ) ?? (
      g.capture(:c_start)
      inner.bytecode(g)
      g.capture(:c_end, captargs)
    )
  }}

  NamedTextCapture << { bytecode: |g| {
    g.capture(:s_start)
    inner.bytecode(g)
    g.capture(:s_end, captargs)
  }}

  NamedTokenCapture << { bytecode: |g| {
    g.capture(:t_start)
    inner.bytecode(g)
    g.capture(:t_end, captargs)
  }}

  Reduction << { bytecode: |g| {
    g.capture(:r_start)
    inner.bytecode(g)
    g.capture(:r_end, captargs)
  }}

  RuleCall << { bytecode: |g| {
    memoize           = true
    result_check      = g.new_label
    memo_result_check = g.new_label
    local_success     = g.new_label
    local_fail        = g.new_label

    # Push @captures onto stack and set @captures to a new empty array
    g.push_temp_captures

    if(memoize) {
      # Check if a memoized result exists;
      # If one does, go straight to memo_result_check label
      g.memo_or_eq_new_hash(name)
      g.goto_if_memo_for_idx(name, memo_result_check)
    }

    # Call the method; sets @captures and returns the result_idx (or nil)
    g.push_self
      g.push_subject
      g.push_idx
    g.send(name, 2)

    # Check result and memoize the result
    result_check.set!
    g.copy_result_to_memo(name)
    g.dup_top
    g.goto_if_true(local_success)
    g.goto(local_fail)

    if(memoize) {
      # Check result, but skip memo copy
      memo_result_check.set!
      g.dup_top
      g.goto_if_true(local_success)
      g.goto(local_fail)
    }

    # Failure, reject index and captures
    local_fail.set!
    g.pop # reject idx (which is actually null here)
    g.pop_to_reject_captures
    g.goto(g.overall_fail)

    # Success, accept new index and captures
    local_success.set!
    g.pop_to_set_idx # set to the new index given by the call's return value
    g.pop_to_accept_captures
  }}
}

}