Developer Forums | About Us | Site Map


Useful Lists

Web Host
site hosted by netplex

Online Manuals

The road to better programming: Chapter 6. Developing cfperl
By Teodor Zlatanov - 2004-03-08 Page:  1 2 3 4


I can't think of anything simpler than trying to schedule a "for fun" project like cfperl. Obviously, there was no pressure to deliver on a certain date, and the requirements were entirely up to me. Why, then, did I come up with a schedule at all? A schedule forces programmers to think a certain way, to have a goal, to deliver a product. So I reluctantly forced a schedule upon myself for my own good. Of course, if you don't want to follow a schedule on a for-fun project, that's okay too. But then you run the very real risk of tweaking it forever without ever actually releasing it.

The schedule for release 1 of cfperl was as follows:

  • December 2001 through January 2002: requirements
  • January 2002 through February 2002: architecture
  • February 2002 through July 2002: coding
  • July 2002 through August 2002: debugging
  • August 2002 through September 2002: documenting

Architecture choices

The architecture of cfperl had to be similar to the architecture of cfengine. I was, after all, trying to emulate the behavior of cfengine. Thus, cfperl had to interpret cfengine configuration files, it had to take at least some of the flags that cfengine takes, and it had to be very flexible in its grammar to allow for changes in the cfengine grammar. I decided to use the Parse::RecDescent module because speed was not of the essence, and flexibility was very important. My experience with Parse::RecDescent also pushed me in that direction. Hand crafting a grammar in Perl instead of using Parse::RecDescent is not a fun exercise for any but the simplest cases. It seems so easy at the beginning, just a while() loop...but then you find out about the comments and the white spaces and how the grammar is different in each configuration section and before you know it, your while() loop is three pages long and doesn't fit in your head anymore. Don't let your friends parse without a grammar.

My experience with other parsers, both hand-crafted ones, and modules like Parse::Yapp, is that they work well for specific purposes, but Parse::RecDescent offers the best integration with Perl. Also, the Perl 6 regular expressions and grammars will be very similar to Parse::RecDescent's grammar, so porting cfperl to Perl 6 will be easy.

With Parse::RecDescent I decided on having layered grammars, meaning that there would be a top level parser (similar to the C precompiler), and the top level parser would hand off specific sections to the parsers responsible for those sections. For instance, the top level parser would be responsible for including external files seamlessly into the configuration, while the parser for the "classes" section would be responsible for defining and undefining classes. I should mention that cfengine has the concept of classes: runtime strings that may be defined or undefined. For example, cfengine defines the class Solaris if you're running on a Solaris machine.

Internally, cfperl stores defined and undefined classes in a Perl hash. The value can be one (defined) or 0 (undefined). Classes are compounded into complex logical statements: Solaris.gemini will only be true if running on a Solaris machine called gemini (or if the gemini class has been otherwise defined). Solaris.!(gemini|jack) means that something should be executed only on a Solaris machine that is called neither gemini nor jack (or if the gemini and jack classes have not been otherwise defined). I had to write a separate parser for these complex logical statements. That was, perhaps, the hardest piece of cfperl I had to write.

I used the AppConfig module to handle command line switches like cfengine does. This choice was based on my experience with AppConfig, and on the similarity between AppConfig's argument parsing and cfengine's argument parsing.

I will explain my other architecture choices as I take you through the cfperl project in the next few chapters.

In the next chapters, I will explain the exact features that cfengine did not provide, since those are the features that cfperl will be implementing. In brief, those are: user management, better file editing (the only cfengine feature I would improve in cfengine itself as well), crontab management, and dynamically executed Perl code.

View The road to better programming: Chapter 6. Developing cfperl Discussion

Page:  1 2 3 4 Next Page: Resources

First published by IBM developerWorks

Copyright 2004-2019 All rights reserved.
Article copyright and all rights retained by the author.