class Bud::PushApplyMethod

A push-based dataflow element that applies a method to a lattice value

Constants

SOURCE_TYPES

Public Class Methods

new(bud_instance, recv, meth, args, blk) click to toggle source
Calls superclass method Bud::LatticePushElement::new
# File lib/bud/lattice-core.rb, line 282
def initialize(bud_instance, recv, meth, args, blk)
  super(bud_instance)
  @recv = recv
  @meth = meth
  @blk = blk
  @args = args.dup
  @is_morph = Bud::Lattice.global_morphs.include? @meth
  @recv_is_scanner = @recv.kind_of? Bud::LatticeScanner

  recv.wire_to(self, :output)
  bud_instance.push_elems[[self.object_id, recv, meth, blk]] = self

  # Arguments that are normal Ruby values are assumed to remain invariant as
  # rule evaluation progresses; hence, we just pass along those values when
  # invoking the function. Arguments that are derived from lattices or
  # collections might change; hence, we need to wire up the push dataflow to
  # have the current values of the function's arguments passed to this node.

  # Map from input node to a list of indexes; the indexes identify the
  # positions in the args array that should be filled with the node's value
  @input_sources = {}

  # Similarly, map from input node to a cached value -- this is the last value
  # we've seen from this input. If the input gave us a delta, we merge
  # together all the deltas we've seen and cache the resulting value.  XXX: In
  # the common case that the input is a scanner over a lattice wrapper, this
  # means we do redundant work merging together deltas.
  @input_caches = {}

  # Inputs for which we haven't seen a value yet.
  @waiting_for_input = Set.new
  @recv_cache = nil
  @seen_recv = false

  @args.each_with_index do |a, i|
    if SOURCE_TYPES.any?{|s| a.kind_of? s}
      if a.kind_of? Bud::LatticeWrapper
        a = a.to_push_elem
      end
      a.wire_to(self, :output)
      @input_sources[a] ||= []
      @input_sources[a] << i
      @waiting_for_input << a
      @args[i] = nil          # Substitute actual value before calling method
    end
  end

  @seen_all_inputs = @waiting_for_input.empty?
end

Public Instance Methods

insert(v, source) click to toggle source
# File lib/bud/lattice-core.rb, line 332
def insert(v, source)
  if source == @recv
    if @seen_recv
      # Update the cached value for the method receiver. Note that if we're
      # applying a method directly to a LatticeScanner (i.e., method applied
      # to lattice wrapper), we can avoid maintaining a separate cache and
      # instead use the wrapper's current value.
      if @recv_is_scanner
        @recv_cache = @recv.collection.current_value
      else
        @recv_cache = @recv_cache.merge(v)
      end
    else
      @recv_cache = v
    end
    @seen_recv = true
    if @seen_all_inputs
      if @is_morph
        recv_val = v
      else
        recv_val = @recv_cache
      end
      res = recv_val.send(@meth, *@args, &@blk)
      push_out(res)
    end
  else
    arg_indexes = @input_sources[source]
    raise Bud::Error, "unknown input #{source}" if arg_indexes.nil?
    arg_val = v
    unless @is_morph
      if @input_caches[source]
        arg_val = @input_caches[source].merge(arg_val)
      end
    end
    arg_indexes.each do |i|
      @args[i] = arg_val
    end

    unless @seen_all_inputs
      @waiting_for_input.delete(source)
      @seen_all_inputs = @waiting_for_input.empty?
    end

    if @seen_all_inputs && @seen_recv
      res = @recv_cache.send(@meth, *@args, &@blk)
      push_out(res)
    end

    if @input_caches.has_key? source
      @input_caches[source] = @input_caches[source].merge(v)
    else
      @input_caches[source] = v
    end
    arg_indexes.each do |i|
      @args[i] = @input_caches[source]
    end
  end
end
inspect() click to toggle source
# File lib/bud/lattice-core.rb, line 391
def inspect
  "#{super} [#{@meth}]"
end