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 <tenderflake@mailinator.com> wrote:

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

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

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

Or, my blogs:

http://rubyinpractice.com

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

···

On Fri, Apr 4, 2008 at 4:49 AM, tender flake <tenderflake@mailinator.com> wrote:

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.