rebinding a Proc

P

Phil Tomson

Weird stuff ahead. I'm making (actually, remaking) a DSL. I'm re-visioning
RHDL to take more advantage of metaprogramming tricks.

So I'd like the user to be able to declare a logic gate like so:

class AndGate < RHDL
inputs :a, :b
inouts :bus
outputs :a_and_b

define_behavior {
puts "Inside of define_behavior: a is: #{a}, b is: #{b}"
a_and_b = a & b
}
end

And then the user should be able to instantiate an AndGate like so:

andg = AndGate.instance() #optional args


The 'inputs', 'inouts' and 'outputs' methods create accessors for the signals
at the class level so that they are then accessable in the block passed to
define_behavior.

Here's some of the background from the RHDL class:

class RHDL
class << self
def instance(*args) #clones this class.
puts "instance:self is: #{self}"
klass = self.clone
puts "klass.class is: #{klass.class}"
klass
end
def define_behavior(&b)
@__behavior = b
puts "@behavior.class is: #{@__behavior.class}"
end
def behavior
@__behavior
end
def run
puts "in #{self}::run"
puts "a is: #{a}, b is: #{b} "
@__behavior.call
end
def inports
@__inports ||=[]
end
def inoutports
@__inoutports ||=[]
end
def outports
@__outports ||=[]
end
def create_accessor str
instance_eval "def #{str}=(val); puts \"self is: #{self}\"; @#{str} =
val; end"
instance_eval "def #{str}; @#{str} ||=0; end"
end
def inputs *in_syms
in_syms.each {|input|
inports << Port.new(input.to_s)
create_accessor input
}
end
#... outputs, inouts, etc.
end


Of course, the problem is in the block passed to the define_behavior method.
It's defined within the context of the AndGate class definition above, so if
we clone the AndGate class itself we find that the behavior is still determined
by the context of the original AndGate class.

So If I do:

AndGate.a=1; AndGate.b=1
AndGate.run #=> 1

a = AndGate.instance()
a.a=0; a.b=0
a.run #=> 1 (but should be 0; this is because the block is still evaluated
in the context of AndGate class, not the clone of AndGate)


So when the AndGate class is cloned, is there a way to unbind the proc in the
class' @__behavior class inst var and rebind it in the context of the new
cloned class? (kind of what can be done with methods and UnboundMethod)

[I realize there are probably other ways to do this where this wouldn't be an
issue (use a method instead of a proc, for example) but I'm trying to maintain
compatibility with the backend simulation guts of the current RHDL system.]

Phil
 
P

Phil Tomson

Sorry to respond to my own message, but sometimes it helps to write these
things out. Here's how I did it (look for changes in the RHDL class below):
So I'd like the user to be able to declare a logic gate like so:

class AndGate < RHDL
inputs :a, :b
inouts :bus
outputs :a_and_b

define_behavior {
puts "Inside of define_behavior: a is: #{a}, b is: #{b}"
a_and_b = a & b
}
end

And then the user should be able to instantiate an AndGate like so:

andg = AndGate.instance() #optional args



class RHDL
class << self
def instance(*args) #clones this class.
puts "instance:self is: #{self}"
klass = self.clone
puts "klass.class is: #{klass.class}"
@ubm.bind(klass) #bind the unboundmethod to the cloned class
meth = klass.method:)do_it) #get a method object from the newly
# bound 'do_it' method.

klass.set_behavior &meth #set cloned class' @__behavior proc
#to the new method object
#('&' converts to proc)
klass
end
def define_behavior(&b)
@__behavior = b
self.class.send:)define_method, :do_it, &b) #define a 'do_it' method
@ubm = self.class.instance_method:)do_it) #create an unbound method
puts "@behavior.class is: #{@__behavior.class}"
end
#add a set_behavior method:
def set_behavior &b
@__behavior = b
end

def behavior
@__behavior
end
def run
puts "in #{self}::run"
puts "a is: #{a}, b is: #{b} "
@__behavior.call
end
def inports
@__inports ||=[]
end
def inoutports
@__inoutports ||=[]
end
def outports
@__outports ||=[]
end
def create_accessor str
instance_eval "def #{str}=(val); puts \"self is: #{self}\"; @#{str} =
val; end"
instance_eval "def #{str}; @#{str} ||=0; end"
end
def inputs *in_syms
in_syms.each {|input|
inports << Port.new(input.to_s)
create_accessor input
}
end
#... outputs, inouts, etc.
end

Now it seems to work:
AndGate.a=1; AndGate.b=1
AndGate.run #=> 1

a = AndGate.instance()
a.a=0; a.b=0
a.run #=> 0 #a now is independent of AndGate


....seems to work, but it also seems a bit convoluted ;-)

Phil
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top