web developer & system programmer

coder . cl

ramblings and thoughts on programming...

implementing DSLs in haskell

published: 28-08-2012 / updated: 28-08-2012
posted in: development, haskell, programming, tips
by Daniel Molina Wegener

Haskell is a purely functional language with a strong-static typing system. That does not means that you cannot build a compiler using Haskell, and you have a wide variety of compiler constructions libraries and tools. The one that I use in many cases is Parsec. Parsec is a monadic combinator based parser generator that can be modified in runtime, and you do not compile its specification, because it is written directly in Haskell using its set of combinators, providing a very dynamic behavior and allowing a wide variety of possible implementations. Recently on the blog www.programando.org, was launched a challenge to construct a DSL parser for L-System specifications.

The specification is not so hard to handle from the Parsec perspective. If you want a faster parsing, you can use Attoparsec instead of Parsec, and if you want compiled parser from BNF like specifications, you must use another parser generator like Happy. An example of the DSL language is as follows:

lar 20
ang 60
axi A
iter 2

If you observe the code, it is just a line oriented DSL, each line contains one instructions, and the “\n” and “\r” characters are instruction delimiters. There is only one variable specifier, that keeps organizing the L-System sequences, allowing the number of iterations to be specified with the iter instruction. So, the basic parser looks as follows — with the evident problem of having a strict evaluation order, but meets the requirement.

data LSysCompSet = LSysCompSet {
  lLar           :: LSLarArg
  , lAng         :: LSAngArg
  , lAxi         :: LSAxiArg
  , lSeqI        :: [LSLSysComp]
  , lIter        :: Integer
  } deriving (Eq, Show)

parLSysCompSet :: Parser LSysCompSet
parLSysCompSet = do
  _ <- parEol
  l <- parLsLar
  a <- parLsAng
  x <- parLsAxi
  s <- many parLSysComp
  r <- parLsIterComp
  _ <- parEol
  return LSysCompSet {
    lLar = l
    , lAng = a
    , lAxi = x
    , lSeqI = s
    , lIter = r

So, the DSL specification is compiled into the LSysCompSet data type, holding the full instructions set of the DSL. In other cases, like processing DSLs, you can perfectly compile the instruction set to calling functions, and even to bytecode for any kind of machine. So, Haskell parser generators are really cool, just imagine the number of options that can have a combinator based parser generator. If you see the code the instructions placed on Haskell code are pretty clear, without offuscated loops and iterations made on top of BNF parser generators. You can find the full parser generator example on for the L-System drawing tool on github here.

No coments yet.

post a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>