Re: domain-specific language



On 9/7/05, Joe Van Dyk <joevandyk@xxxxxxxxx> wrote:
> On 9/7/05, Joe Van Dyk <joevandyk@xxxxxxxxx> wrote:
> > On 8/18/05, Jacob Fugal <lukfugl@xxxxxxxxx> wrote:
> > > > node :node1 do
> > > > ip 192.whatever
> > > > title "Node 1"
> > > > end
> > > >
> > > > node :node2 do
> > > > ip 192.whatever
> > > > title "Node 2"
> > > > end
> > >
> > > <snip>
> > >
> > > > class ClusterManager
> > > >
> > > > def load_config_file config_file
> > > > instance_eval File.read(config_file)
> > > > end
> > > >
> > > > def node node_id, &block
> > > > # What goes here?
> > > > end
> > > >
> > > > end
> > >
> > > I'd think similarly, but a bit different in that
> > > ClusterManager::Builder should be the one doing the instance eval:
> > >
> > > class ClusterManager
> > > attr_accessor :nodes
> > >
> > > def initialize
> > > @nodes = []
> > > end
> > >
> > > def describe
> > > @nodes.collect{ |node| node.describe }.join("\n")
> > > end
> > >
> > > class Node
> > > attr_accessor :id, :ip, :title
> > >
> > > def initialize( id )
> > > @id = id
> > > end
> > >
> > > def describe
> > > "#@id -> #@ip \"#@title\""
> > > end
> > >
> > > class Builder
> > > def process( node, dsl )
> > > @node = node
> > > instance_eval &dsl
> > > end
> > >
> > > def ip( value )
> > > @node.ip = value
> > > end
> > >
> > > def title( value )
> > > @node.title = value
> > > end
> > > end
> > > end
> > >
> > > class Builder
> > > def process( manager, dsl )
> > > @manager = manager
> > > @node_builder = Node::Builder.new
> > > instance_eval &dsl
> > > end
> > >
> > > def node( node_id, &block )
> > > node = Node.new( node_id )
> > > @node_builder.process( node, block )
> > > @manager.nodes << node
> > > end
> > > end
> > > end
> > >
> > > dsl = lambda{
> > > node :node1 do
> > > ip '192.whatever'
> > > title 'Node 1'
> > > end
> > >
> > > node :node2 do
> > > ip '192.whatever'
> > > title 'Node 2'
> > > end
> > > }
> > >
> > > manager = ClusterManager.new
> > > builder = ClusterManager::Builder.new
> > > builder.process( manager, dsl )
> > >
> > > puts manager.describe
> > >
> > > ###############
> > >
> > > A few caveats about the above:
> > >
> > > 1) You'll notice I changed the DSL a little (quoted the 192.whatever
> > > values). That was simply for brevity in this illustration.
> > >
> > > 2) My Builders require the argument to process be lambdas (Procs) not
> > > strings. This was because I didn't want to complicate things with
> > > switches on whether to use the prefix & or not. But it would be as
> > > simple as an if/else to hide the lambda/string distinction from the
> > > "user" (which will be yourself, not the person writing in the DSL)
> > > inside the Builder. Alternatively, you can take a string and build it
> > > into a lambda for the Builder via eval("lambda{ #{dsl} }"). I wouldn't
> > > necessarily recommend that though (with all the evils of eval).
> >
> > Thank you for your ideas. I've adapted it somewhat for parts of my
> > application, but I'm running into problems.
> >
> > I've got the following configuration file:
> >
> > option :display do
> > display :text, :size => 20, :title => "DISPLAY"
> > value :DISPLAY, :default => ENV['DISPLAY'] || 'localhost:0'
> > end
> >
> > argument :xterm_title do
> > display :text, :size => 20, :title => "Xterm Title"
> > value "-T", :default => "You are on a xterm!"
> > end
> >
> > argument :xterm_text_color do
> > display :text, :size => 20, :title => "Xterm Font Color"
> > value "-fg", :default => "red"
> > end
> >
> >
> > application :xterm do
> > executable "/usr/X11R6/bin/xterm"
> > title :Xterm
> > node :fatire
> > options :display
> > arguments :xterm_text_color, :xterm_title
> > end
> >
> > application :xeyes do
> > executable "/usr/X11R6/bin/xeyes"
> > title :Xeyes
> > node :fatire
> > options :display
> > end
> >
> >
> > Should be self-explanatory. I have some applications that take env
> > options and command-line arguments, and I want to have a very readable
> > and configurable file for defining those applications, options, and
> > arguments (and some other things). The 'node' option for the
> > application is the machine that the application should be started on.
> > The 'display' option for the options/arguments state how the GUI
> > should display the option/argument.
> >
> > My problem is trying to adapt your solution to fit something like
> > this. There would be a lot of duplication if I had Builders for
> > applications, nodes, options, and arguments. And I'm also having some
> > difficulties getting each Application object to know what options and
> > arguments it should have.
> >
> > I considered using YAML for the configuration file format, but I'm
> > still leaning towards having a pure Ruby file.
> >
> > Thoughts are greatly appreciated! This is my first time trying to do
> > this type of programming, so it's a little weird.
> >
>
>
> I would also like to be able to 'group' things together, like
>
> group :xterm_options_arguments do
> arguments :xterm_title, :xterm_text_color
> options :display
> end
>
> application :xterm do
> group :xterm_options_arguments
> title :Xterm
> ...
> end
>
> So that applications with common options and arguments can specify a grouping.
>
> (btw, an 'argument' is a command-line argument, like 'ls -F'... the -F
> is an argument. An option is something like "DISPLAY=my_machine:1
> xeyes".. the DISPLAY is an option).

And grouping of applications would also be nice:

application_group :all_x_apps
applications :xterm, :xeyes
display :checkbox, :title => "Start all X applications"
end

So, in the GUI, there would be a checkbox titled "Start all
applications" that would start the xterm and xeyes applications.

In other words, I'd like to be able to do grouping of various things
in this configuration file.


.



Relevant Pages