The Artima Developer Community
Sponsored Link

Ruby Community News Forum
Two Ways to Build a DSL

2 replies on 1 page. Most recent reply: Oct 8, 2006 5:49 PM by russ olsen

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 2 replies on 1 page
Frank Sommers

Posts: 2642
Nickname: fsommers
Registered: Jan, 2002

Two Ways to Build a DSL Posted: Oct 5, 2006 12:56 PM
Reply to this message Reply
Summary
As Russ Olsen points out in a recent blog post, there are two ways to build a DSL (domain-specific language): writing a parser for the language, or bending an existing language into a DSL. Olsen provides a thorough example of how to do the latter in Ruby.
Advertisement

Domain-specific languages, or DSLs, have recently become popular as a way to improve developer productivity: A well-designed DSL provides just the language-features needed for a domain-specific task, removing the verbosity of a more general programming language.

The relative merits of DSLs have been discussed on these pages before. And Jim Freeze has highlighted the advantages of using Ruby when implementing a DSL in an article in Artima's Ruby Code & Style, Creating DSLs with Ruby.

More recently, Russ Olsen gave an concise example of how to mold a Ruby program into interpreting a DSL. As Olsen points out, there are two general approaches to defining a DSL:

There are at least two ways to skin the DSL cat. First, you can ... create a parser for your language by hand or by using a tool like ANTLR or YACC or RACC or even do what the authors of ant did and and define your language as a flavor of XML....

[The other approach is] one in which you start with some implementation language, perhaps Ruby, and you simply bend that one language into being your domain specific language. If you are using Ruby to implement your internal DSL, then anyone who writes a program in your little language is actually, and perhaps unknowingly, writing a Ruby program.

The latter approach, which is used in Ruby on Rails, for instance, involves the following general steps, according to Olsen:

Start off by defining your data structures... Next, set up some top level methods which will support the actual DSL language... Next suck in the DSL [program] text with a load statement. Typically the effect of pulling in the DSL text is to fill in your data structures... Finally, after the load, you do whatever it is that the user asked you to do. How do you know what to do? Why by looking in those freshly populated data structures.

Olsen walks his readers through a DSL that evaluates the answers users provide to a set of questions:

question 'Who was the first president of the USA?'
wrong 'Fred Flintstone'
wrong 'Martha Washington'
right 'George Washington'
wrong 'George Jetson'

Olsen observes that the above DSL code snippet is actually valid Ruby syntax, where question, right, and wrong can be interpreted as method names, and the question and answer text as method parameters. Thus a trivial Ruby program to interpret the above DSL is as follows, according to Olsen:

def question(text)
  puts "Just read a question: #{text}"
end

def right(text)
  puts "Just read a correct answer: #{text}"
end

def wrong(text)
  puts "Just read an incorrect answer: #{text}"
end

load 'questions.qm'

This program takes advantage of the Ruby load keyword that loads and then immediately executes the content of the specified file, assuming that the file contains valid Ruby code.

In subsequent iterations, Olsen extends this simple program to load data structures with questions and answers as the program executes, and implements fairly simple logic in a Question and Answer pair of classes to count the number of right and wrong answers the user provided:


class Question

  def initialize( text )
    @text = text
    @answers = []
  end

  def add_answer(answer)
    @answers << answer
  end

  def ask
    puts ""
    puts "Question: #{@text}"
    @answers.size.times do |i|
      puts "#{i+1} - #{@answers[i].text}"
    end
    print "Enter answer: "
    answer = gets.to_i - 1
    return @answers[answer].correct
  end
end

class Answer
  attr_reader :text, :correct
  def initialize( text, correct )
    @text = text
    @correct = correct
  end
end

What is your preferred way of implementing a DSL: with a custom parser, or as a superset of a programming language, as in Olsen's example?


Eivind Eklund

Posts: 49
Nickname: eeklund2
Registered: Jan, 2006

Re: Two Ways to Build a DSL Posted: Oct 6, 2006 2:11 AM
Reply to this message Reply
In Ruby: As Olsen does it.
In anything else I've programmed in: Create a parser for it.

This is the "normal case" for each of those, though - I have done it the other way for both, because that specific case was better implemented the other way.

However, the ease of creating nice DSLs is one of Ruby's greatest strengths.

russ olsen

Posts: 1
Nickname: rolsen
Registered: Oct, 2006

Re: Two Ways to Build a DSL Posted: Oct 8, 2006 5:49 PM
Reply to this message Reply
For those interested, the oringal can be found at:
http://www.jroller.com/page/rolsen?entry=building_a_dsl_in_ruby

Flat View: This topic has 2 replies on 1 page
Topic: Charles Nutter on Why JRuby Needed a New Interpreter Previous Topic   Next Topic Topic: Does Convention over Configuration Mostly Help Green Field Projects?

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use