DSL implementation question

Hello,

I’m trying to write a simple DSL to define memory maps of hardware
devices.
Something like:

cfg_block “top_device”, :depth => 16 do
reg “ctrl_reg”, :offset => 0, :mode => “RW”
reg “status_reg”, :offset => 1, :mode => “R”
end

I’ve implemented something but I am not satisfied with it.

The problem I have is about scoping. How can I make the reg command
in my
small DSL exemple above execute in the context of the object created
by the
cfg_block method? The way I implemented this is by passing the
created
cfg_block object as a block parameter and then passing it to every reg
command. E.g:

cfg_block “top_device”, :depth => 16 do |dev|
reg “ctrl_reg”, dev, :offset…
end

I consider this redundant since I’d like the context to provide the
cfg_block reference. I tried multiple things but haven’t figured it
out
using blocks. Is there a way to get rid of dev?

Suggestions would be greatly appreciated. Thanks in advance.

My implementation follows below:

module Register_defs

class Cfg_Block < Hash

  def initialize(name, params={})
     self[:name] = name
     self[:regs] = {}
     self.merge!(params)
  end

  def add_reg(reg)
     self[:regs][reg[:name]] = reg
  end

end

class Register < Hash
def initialize(name, cfg_block, params={})
self[:name] = name
self.merge!(params)
cfg_block.add_reg(self)
end

end

def cfg_block(name, params={})
cfg_block = Cfg_Block.new(name, params)
yield(cfg_block)
cfg_block
end

def reg(name, cfg_block, params={})
reg = Register.new(name, cfg_block, params)
yield(reg)
reg
end

end

include Register_defs

modules = cfg_block “top_block” do |cb|

reg “reg1”, cb, :addr => 0 do
# do something
end

reg “reg2”, cb, :addr => 0 do
# do something else
end

end

p modules

You’d do something like this:

def cfg_block(name, params={}, &block)

Other stuff

my_obj.instance_eval &block
end

…where my_obj is the object that you were previously yielding. :slight_smile:

–Jeremy

On Thu, Apr 3, 2008 at 11:49 PM, tender flake
[email protected] wrote:

  end
     self.merge!(params)

modules = cfg_block “top_block” do |cb|

p modules


http://jeremymcanally.com/
http://entp.com

Read my books:
Ruby in Practice (Ruby in Practice)
My free Ruby e-book (http://humblelittlerubybook.com/)

Or, my blogs:

http://rubyinpractice.com

On Fri, Apr 4, 2008 at 4:49 AM, tender flake
[email protected] wrote:

I’ve implemented something but I am not satisfied with it.

Hi,

I wrote Doodle to do this kind of thing (gem install doodle):

require ‘doodle’
require ‘pp’

class Register < Doodle::Base
has :name
has :offset
has :mode
end

class ConfigBlock < Doodle::Base
has :name
has :depth
has :registers, :init => [], :collect => { :reg => Register }
end

def cfg_block(*args, &block)
ConfigBlock(*args, &block)
end

modules = cfg_block “top_device”, :depth => 16 do
reg “ctrl_reg”, :offset => 0, :mode => “RW”
reg “status_reg”, :offset => 1, :mode => “R”
end

pp modules

Output:

#<ConfigBlock:0xb7b9b5c4
@depth=16,
@name=“top_device”,
@registers=
[#<Register:0xb7b9582c @mode=“RW”, @name=“ctrl_reg”, @offset=0>,
#<Register:0xb7b8ecfc @mode=“R”, @name=“status_reg”, @offset=1>]>

If you wanted validation on the parameters, you could do something like:

require ‘doodle’

class Named < Doodle::Base
has :name, :kind => String do
from Symbol do |sym|
sym.to_s
end
end
end

class Register < Named
has :offset, :kind => Integer do
must “be in range 0-15” do |offset|
(0…15).include?(offset)
end
end
has :mode, :kind => String do |mode|
valid_modes = %w[R W RW]
must “be one of #{valid_modes.join(', ')}” do |mode|
valid_modes.include?(mode)
end
end
end

class ConfigBlock < Named
has :depth, :kind => Integer
has :registers, :init => [], :collect => { :reg => Register }
end

def cfg_block(*args, &block)
ConfigBlock(*args, &block)
end

modules = cfg_block :top_device, :depth => 16 do
reg :ctrl_reg, :offset => 0, :mode => “RW”
reg :status_reg, :offset => 1, :mode => “WR”
end

#=> memory-map2.rb:35: mode must be one of R, W, RW - got String(“WR”)
(Doodle::ValidationError)

Regards,
Sean