The Artima Developer Community
Ruby Code & Style | Discuss | Print | Email | First Page | Previous | Next
Sponsored Link

Ruby Code & Style
Creating DSLs with Ruby
by Jim Freeze
March 16, 2006

Page 1 of 4  >>


Broadly speaking, there are two ways to create a DSL. One is to invent a syntax from scratch, and build an interpreter or compiler. The other is to tailor an existing general-purpose language by adding or changing methods, operators, and default actions. This article explores using the latter method to build a DSL on top of Ruby.

A DSL, or domain specific language, is a (usually small) programming or description language designed for a fairly narrow purpose. In contrast to general-purpose languages designed to handle arbitrary computational tasks, DSLs are specific to particular domains. You can create a DSL in two basic ways:

  1. Invent a DSL syntax from scratch, and build an interpreter or compiler.
  2. Tailor an existing general-purpose language by adding or changing methods, operators, and default actions.

An advantage to the second approach is that you save time because you don't have to write and debug a new language, leaving you more time to focus on the problem confronting the end-user. Among the disadvantages is that the DSL will be constrained by the syntax and the capabilities of the underlying general-purpose language. Furthermore, building on another language often means that the full power of the base language is available to the end-user, which may be a plus or minus depending on the circumstances. This article explores using the second approach to build a DSL on top of Ruby.

Describing Stackup Models

At my job, where I work as an interconnect modeling engineer, we needed a way to describe the vertical geometric profile of the circuitry on a semiconductor wafer. Descriptions were kept in a stackup model file (We coined the word stackup because of the logical way the metal wires are constructed by stacking layers on top of each other in the fabrication process). The issue was that each vendor had their own format for describing a stackup; but we wanted a common format so we could convert between the various file types. In other words, we needed to define a common stackup DSL and write a program that could export from our stackup format to any of the other vendors’ stackup format.

The vendors did not use a sophisticated DSL language, but instead their languages contained only static data elements in a mostly flat textual database. Their file formats did not allow for parameterized types, variables, constants, or equations. Just static data. Further, the format was overly simple. It was either line based or block based with one level of hierarchy.

We started out describing our stack-up format with limited ambition since we only needed to meet the vendors’ level of implementation. But once we saw the benefit of having a more expressive language, we quickly augmented our format. Why were we able to do this, but not the vendors? I believe it was because we used Ruby as a DSL, rather than start from scratch using C as the vendors did. Granted, other languages could have been used, but I don’t think the finished product would have been as elegant; the selection of the general-purpose language is a critical step.

I also believe the vendors’ development speed was handicapped from using C, prompting them to keep their stackup syntax simple to parse. Perhaps, not coincidentally, many of the vendors used simple syntax constructs in their file formats common to many DSLs. Because they occur so often, we are going to first look at how we can mimic these in Ruby before we move into more sophisticated language constructs.

Line-based and Block-level DSL Constructs

Line-based constructs are a way to assign a value or a range of values to a parameter. Among the vendors’ files we were looking at, the following formats were used:

  1. parameter = value
  2. parameter value
  3. parameter min_value max_value step_value

Formats 1 and 2 are equivalent except for the implied ’=’ in 2. Format 3 assigned a range of values to the parameter.

The more complicated formats contained a block construct. The two that we encountered could be manually parsed with a line-based parser and a stack, or a key-letter and word parser with a stack. These two formats are illustrated below:

  type = TYPE
  name = NAME
  param1 = value1
  param2 = value2

One of the block formats used “C” style curly braces to identify a block, but parameter/value pairs were separated by white space.

TYPE NAME {param1 = value1 param2 = value2 }

Page 1 of 4  >>

Ruby Code & Style | Discuss | Print | Email | First Page | Previous | Next

Sponsored Links

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