Data file formats for TCL scripts

Home > Software > TCL data formats



Introduction

A typical TCL script stores its internal data in lists and arrays (the two primary data structures in TCL). Suppose you want to write a TCL application that can save its data on disk and read it back again. For example, this allows your users to save a project and load it back later. You need a way to write the data from the place where it is stored internally (lists and arrays) to a file. You also need a way to read the data back into a running script.

You can choose to store the data in a binary form, or in a text file. This paper is limited to textual data file formats. We will look at a number of possible formats and how to parse them in TCL. In particular, we will show some simple techniques that make text file parsing a lot easier.

This paper assumes that you are familiar with TCL, and that you have written at least a few simple scripts in TCL.

A simple example

Suppose you have a simple drawing tool that places text and rectangle items on a canvas. To save the resulting pictures, you want a textual file format that must be easy to read. The first and simplest file format that comes to mind, looks something like this:

   example1/datafile.dat
   rectangle 10 10 150 50 2 blue
   rectangle 7 7 153 53 2 blue
   text 80 30 "Simple Drawing Tool" c red

The first two lines of this file represent the data for two blue, horizontally stretched rectangles with a line thickness of 3. The final line places a piece of red text, anchored at the center (hence the "c"), in the middle of the two rectangles.

Saving your data in a text file makes it easier to debug the application, because you can inspect the output to see if everything is correct. It also allows users to manually tinker with the saved data (which may be good or bad depending on your purposes).

When reading a data file in this format, you somehow need to parse the file and create data structures from it. To parse the file, you may be tempted to step through the file line by line, and use something like regexp to analyse the different pieces of the text. This is one possible implementation:

   example1/parser.tcl
   canvas .c
   pack .c

   set fid [open "datafile.dat" r]
   while { ![eof $fid] } {
      # Read a line from the file and analyse it.
      gets $fid line

      if { [regexp \
         {^rectangle +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +(.*)$} \
            $line dummy x1 y1 x2 y2 thickness color] } {
         .c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color

      } elseif { [regexp \
         {^text +([0-9]+) +([0-9]+) +("[^"]*") +([^ ]+) +(.*)$} \
         $line dummy x y txt anchor color] } {
         .c create text $x $y -text $txt -anchor $anchor -fill $color

      } elseif { [regexp {^ *$} $line] } {
         # Ignore blank lines

      } else {
         puts "error: unknown keyword."
      }
   }
   close $fid
We read one line at a time, and use regular expressions to find out what kind of data the line represents. By looking at the first word, we can distinguish between data for rectangles and data for text. The first word therefor serves as a keyword: it tells us exactly what kind of data we are dealing with. We also parse the coordinates, color and other attributes of each item. Grouping parts of the regular expression between parentheses allows us to retrieve the parsed results in the variables 'x1', 'x2', etc.

This looks like a simple enough implementation, assuming that you understand how regular expressions work. But I find it pretty hard to maintain. The regular expressions also make it hard to understand.

There is a more elegant solution, known as an 'active file'. It is captured in a design pattern, originally written by Nat Pryce. It is based on a very simple suggestion: Instead of writing your own parser in TCL (using regexp or other means), why not let the TCL parser do all the work for you?

The Active File design pattern

To explain this design pattern, we continue the example of the simple drawing tool from the previous section. First, we write two procedures in TCL, one that draws a rectangle, the other writes text.

   example2/parser.tcl
   canvas .c
   pack .c

   proc d_rect {x1 y1 x2 y2 thickness color} {
      .c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
   }

   proc d_text {x y text anchor color} {
      .c create text $x $y -text $text -anchor $anchor -fill $color
   }
To make a picture on the canvas, we can now call these two procs several times, once for each item we want to draw. To make the same picture as above, we need the following three calls:
   example2/datafile.dat
   d_rect 10 10 150 50 2 blue
   d_rect 7 7 153 53 2 blue
   d_text 80 30 "Simple Drawing Tool" c red

Does this look familiar? The code for calling our two procs looks almost exactly like the data file we parsed earlier. The only difference is that the keywords have changed from 'rectangle' and 'text' to 'd_rect' and 'd_text'.

Now we come to the insight that makes this design pattern tick: to parse the data file, we treat it like a TCL script. We just put the calls to our two procedures in a file, and we use that as the data file. The fact that the data file actually contains calls to TCL procedures, is the heart of this design pattern.

Parsing the data file is now extremely easy:

   source "datafile.dat"
The built-in TCL command source reads the file, parses it, and executes the commands in the file. Since we have implemented the procedures d_rect and d_text, the source command will automatically invoke the two procedures with the correct parameters. We will call d_rect and d_text the parsing procedures.

We do not need to do any further parsing. No regular expressions, no line-by-line loop, no opening and closing of files. Just one call to source does the trick.

The data file has become a TCL script that can be executed. This is called an Active File because it contains executable commands, not just passive data. The Active File design pattern works in most scripting languages, and is excellently described by >> Nat Pryce on his website.

Advantages of using the Active File pattern:

Disadvantages of using the Active File pattern:

Limitations of the Active File pattern:

The English proc

So far we have been able to come up with a very simple file format:

   d_rect 10 10 150 50 2 blue
   d_rect 7 7 153 53 2 blue
   d_text 80 30 "Simple Drawing Tool" c red

And we have a very simple parser for it, using only two parsing procedures and the source command. Now, let's see how we can improve things.

When you look at large volumes of this kind of data, it is easy to get confused by all the numbers. The first line contains the numbers 10 10 110 50 3, and it takes some training to quickly see the first two as a pair of coordinates, the next two as another pair, and the last one as the line thickness. We can make this easier to read for a programmer by introducing some additional text in the data:

   example3/datafile.dat
   d_rect from 10 10 to 150 50 thick 2 clr blue
   d_rect from 7 7 to 153 53 thick 2 clr blue
   d_text at 80 30 "Simple Drawing Tool" anchor c clr red
Prepositions like to and from, and argument names like thick and color make the data look more like an English sentence. To accomodate these new prepositions, our parsing procedure needs to get some additional arguments:
   example3/parser.tcl
   proc d_rect {from x1 y1 to x2 y2 thick thickness clr color} {
      .c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
   }
As you can see, the implementation does not change. The new arguments are not used in the procedure's body; their only purpose is to make the data more readable. I make it a habit to make the names of the proc's arguments the same as the corresponding preposition in the data file (e.g. from appears at the same place in the data file and in the argument list). That way, I can quickly see how one maps to the other.

Introducing dummy arguments for readability is called the 'English proc' idiom. We will see other ways of making data more readable.

Option/value pairs

The Tk toolkit offers a set of widgets to create graphical interfaces. These widgets are configured with options and their values. The syntax for the configuration is simple (a dash, followed by the option name, followed by the value) and standardized (many other Tcl extensions use the same syntax for configuring their components).

With option/value pairs, our data file looks like this:

   example4/datafile.dat
   d_rect -x1 10 -y1 10 -x2 150 -y2 50 -thickness 2
   d_rect -thickness 2 -x1 7 -y1 7 -x2 153 -y2 53
   d_text -x 80 -y 30 -text "Simple Drawing Tool" -anchor c -color red
To parse this data, we need to introduce the parsing of option/value pairs in the parsing procedures d_rect and d_text. Our first attempt is to use dummy arguments similar to the English proc idiom:
   proc d_rect {opt1 x1 opt2 y1 opt3 x2 opt4 y2 opt5 thickness opt6 color} {
      .c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
   }
Again, the implementation does not change. It is clear though, that this solution will only work for the simplest of data formats. It has two major disadvantages:

Here is an implementation that solves both these problems:

   example4/parser.tcl
   proc d_rect {args} {
      # First, specify some defaults
      set a(-thickness) 1
      set a(-color) blue

      # Then, 'parse' the user-supplied options and values
      array set a $args

      # Create the rectangle
      .c create rectangle $a(-x1) $a(-y1) $a(-x2) $a(-y2) \
         -width $a(-thickness) -outline $a(-color)
   }
Instead of a long list of arguments, the parsing procedure now only has one argument called args, which captures all the actual parameters of the call. The arguments x1, y1 etc have disappeared. They are now handled by a local array, as we will shortly explain.

The first part of the code sets the default values for some options. The second part parses option/value pairs from args. This is done very elegantly with the TCL built-in array set mechanism. It creates new entries in the array a, using the option names (including the leading dash) as keys into the array, and the option values as the array values.

If the user does not specify -color in the call, the default value of the a(-color) entry still stands. The final line in the proc body is the same as in the previous implementations, except that it now uses array entries rather than procedure arguments.

If the user forgets to specify option -x1 in the call, the array entry for -x1 is not set (there is no default for it) and the call to create rectangle results in an error. This example shows that you can give default values to some options, making them optional, while leaving others mandatory by not specifying defaults for them.

The best format is usually a combination

Now that we have seen some commonly known tricks for TCL data files (Active File, English proc, option/value pairs), we can combine the advantages of each into a single data format. For the mandatory options, we should use fixed-position arguments, perhaps combined with dummy prepositions for readability (as in the English proc). All the optional options on the other hand, should be handled with the option/value pair mechanism, so that users can leave the options out or change their positions in the call. The final format could then look something like this:

   d_rect from 10 10 to 150 50 -thickness 2
   d_rect from 7 7 to 153 53 -thickness 2
   d_text at 60 30 "Simple Drawing Tool" -anchor c -color red
assuming that 'blue' is the default color for all items.

As a personal convention, I usually write such commands as follows:

   d_rect \
      from 10 10 \
      to 150 50 \
      -thickness 2
   d_rect \
      from 7 7 \
      to 153 53 \
      -thickness 2
   d_text \
      at 80 30 "Simple Drawing Tool" \
      -anchor c \
      -color red
I find it slightly more readable, but that's all a matter of personal taste (or in my case lack of taste :-).


More complicated data

So far, we have worked on a very simple example involving only rectangles and text on a canvas. The data format was easy to read and easy to parse using the Active File design pattern.

We will now move to a more complex data format, to explain more 'advanced' techniques for using the Active File pattern in TCL. This will make you an expert in TCL data file formats.

The repository tool

I used to collect design patterns. I made a repository of patterns, each with a brief description and some properties. I also kept the names, authors and ISBN numbers of the books in which I found the patterns, as a reference to be able to look them up later. To keep track of all this information, I implemented a repository tool in TCL. It had features to organize patterns into categories and levels, and to point from each pattern to the book and page number where it was described.

The input to the tool was a file that looked like this:


   # First, I describe some books in which you can find good design patterns
   # and programming idioms.  Each book, website or other source of patterns
   # is specified with the 'Source' keyword, followed by a unique tag and some
   # additional information.

   Source GOF {
     Design patterns
     Elements of reusable object-oriented software
     Gamm, Helm, Johnson, Vlissides
     Addison-Wesley, 1995
     0 201 63361 2
   }

   Source SYST {
     A system of patterns
     Pattern-oriented software architecture
     Buschmann, Meunier, Rohnert, Sommerlad, Stal
     Wiley, 1996
     0 471 95869 7
   }

   # Next, I describe some categories.  I want to group patterns
   # in categories so I can find them back more easily.  Each category
   # has a name (such as "Access control") and a short description.

   Category "Access control" {
      How to let one object control the access to one or more
      other objects.
   }

   Category "Distributed systems" {
      Distributing computation over multiple processes, managing
      communication between them.
   }

   Category "Resource handling" {
      Preventing memory leaks, managing resources.
   }

   Category "Structural decomposition" {
      To break monoliths down into indpendent components.
   }

   # Finally, I describe the patterns themselves.  Each of them has a name,
   # belongs to one or more categories, and occurs in one or more of the
   # pattern sources listed above.  Each pattern has a level, which can
   # be 'arch' (for architectural patterns), 'design' for smaller-scale
   # design patterns, or 'idiom' for language-specific patterns.

   Pattern "Broker" {
     Categories {"Distributed systems"}
     Level arch 
     Sources {SYST:99}   ; # This means that this pattern is described in
                           # the book with tag 'SYST' on page 99.
     Info {
       Remote service invocations.
     }
   }

   Pattern "Proxy" {
     # This pattern fits in two categories:
     Categories {"Access control" "Structural decomposition::object"}
     Level design
     # Both these books talk about the Proxy pattern:
     Sources {SYST:263 GOF:207}
     Info {
       Communicate with a representative rather than with the
       actual object.
     }
   }

   Pattern "Facade" {
     Categories {"Access control" "Structural decomposition::object"}
     Sources {GOF:185}
     Level design
     Info {
       Group sub-interfaces into a single interface.
     }
   }

   Pattern "Counted Pointer" {
     Categories {"Resource handling"}
     Level idiom
     Sources {SYST:353}
     Info {
       Reference counting prevents memory leaks.
     }
   }

This is only a part of the input file I originally worked with, but it already contains enough data to serve as a good example. The descriptions of the patterns are short and pretty stupid, but that's ok for this example.

As you can see, this data file has a number of interesting new features:

You may think that this format is a lot more complicated than the one in our previous example, and that it is near to impossible to write a parser for this in TCL. It may not seem straightforward, we can use the Active File pattern again, making the task a lot simpler. The parsing procedures are a bit more elaborate than before, but they are definitely not "complicated".

Here's the part of my tool that parses a data file such as the one above:


   # We will internally store the data in these three lists:
   set l_patterns [list]
   set l_sources [list]
   set l_categories [list]

   # We also need a variable to keep track of the Pattern structure we are
   # currently in:
   set curPattern ""

   # This is the parsing proc for the 'Source' keyword.
   # As you can see, the keyword is followed by an id (the unique tag for the
   # source), and some textual description of the source.
   proc Source {id info} {
      # Remember that we saw this source.
      global l_sources
      lappend l_sources $curSource

      # Remember the info of this source in a global array.
      global a_sources
      set a_sources($curSource,info) $info
   }

   # The parsing proc for the 'Category' keyword is similar.
   proc Category {id info} {
      global l_categories
      lappend l_categories $curCategory

      global a_categories
      set a_categories($curCategory,info) $info
   }

   # This is the parsing proc for the 'Pattern' keyword.
   # Since a 'Pattern' structure can contain sub-structures,
   # we use 'uplevel' to recursively handle those.
   proc Pattern {name args} {
      global curPattern
      set curPattern $name   ; # This will be used in the sub-structures
                               # which are parsed next
      global l_patterns
      lappend l_patterns $curPattern

      # We treat the final argument as a piece of TCL code.
      # We execute that code in the caller's scope, to parse the elements
      # of the structure.
      # 'uplevel' will call 'Categories', 'Level' and other commands that
      # handle the sub-structures.
      # This is similar to how we use the 'source' command to parse the entire
      # data file.
      uplevel 1 [lindex $args end]

      set curPattern ""
   }

   # The parsing proc for one of the sub-structures.  It is called
   # by 'uplevel' when the 'Pattern' keyword is handled.
   proc Categories {categoryList} {
      global curPattern   ; # We access the global variable 'curPattern'
                            # to find out inside which structure we are.
      global a_patterns
      set a_patterns($curPattern,categories) $categoryList
   }

   # The following parsing procs are for the other sub-structures
   # of the Pattern structure.

   proc Level {level} {
      global curPattern
      global a_patterns
      set a_patterns($curPattern,level) $level
   }

   proc Sources {sourceList} {
      global curPattern
      global a_patterns
      # We store the codes such as 'SYST:99' in a global array.
      # My implementation uses regular expressions to extract the source tag
      # and the page number from such a code (not shown here).
      set a_patterns($curPattern,sources) $sourceList
   }

   proc Info {info} {
      global curPattern
      global a_patterns
      set a_patterns($curPattern,info) $info
   }

At first sight, this may seem to take much more work than what we did for the simple canvas example. But think of the power of this technique. With only a few parsing procedures and by making clever use of the uplevel command, we can parse data files with intricate structure, containing comments, nested sub-structures and freeform textual data. Imagine writing a parser for this from scratch.

The data is parsed by the procedures such as Source, Pattern or Info. The parsed data is stored internally in three lists and three arrays. The nestedness of the data is handled by calls to uplevel, and by remembering in which 'scope' we currently are using the variable curPattern.

Note that this technique requires that your data follows TCL syntax. This implies, among other things, that opening curly braces should be placed at the end of a line, not on the beginning of the next line.

Recursive structures

In the pattern repository example, the structures of type Pattern contain sub-structures of other types such as Info and Sources. What happens when a structure contains sub-structures of the same type? In other words, how do we handle recursive structures?

Suppose, for example, that you want to describe the design of an object-oriented system, which is divided recursively into subsystems:

   example6/datafile.dat
   # Description of an object-oriented video game
   System VideoGame {
     System Maze {
       System Walls {
         Object WallGenerator
         Object TextureMapper
       }
       System Monsters {
         Object FightingEngine
         Object MonsterManipulator
       }
     }
     System Scores {
       Object ScoreKeeper
     }
   }
To keep track of which System structure we are currently in, it seems that we need more than just a single global variable like currPattern. At any point during parsing, we can be inside many nested System structures, so we need more than one variable. We probably need some kind of stack, on which we push a value when we enter the System parsing proc, and from which we pop again at the end of the parsing proc. We can make such a stack using a TCL list.

But if you do not want to maintain a stack, there is a simple way of avoiding that. It is again based on a very simple suggestion: When you need a stack, see if you can use the function call stack. When dealing with such recursive data, I usually implement my parsing procedures like this:

   example6/parser.tcl
   set currSystem ""

   proc System {name args} {
     # Instead of pushing the new system on the 'stack' of current systems,
     # we remember it in a local variable, which ends up on TCL's
     # function call stack.
     global currSystem
     set tmpSystem $currSystem
     set currSystem $name   ; # Thanks to this, all sub-structures called by
                              # 'uplevel' will know what the name of their
                              # immediate parent System is

     # Store the system in an internal data structure
     # (details not shown here)
     puts "Storing system $currSystem"

     # Execute the parsing procedures for the sub-systems
     uplevel 1 [lindex $args end]

     # Pop the system off the 'stack' again.
     set currSystem $tmpSystem
   }

   proc Object {name} {
     global currSystem
     # Store the object in the internal data structure of the current
     # system (details not shown here)
     puts "System $currSystem contains object $name"
   }

   source "datafile.dat"

Instead of storing the nested system names on a stack (simulated by a TCL list or array I suppose), we just store the names in local variables called tmpSystem. Since the parsnig procedures are automatically called in a stack-based order by TCL, we do not need to explicitly push/pop anything.

Other examples

The CGI library by Don Libes uses the Active File pattern to represent HTML documents. The idea is that you write a TCL script that acts as an HTML document and generates pure HTML for you. The documents contain nested structures for bulleted lists, preformatted text and other HTML elements. The parsing procedures call uplevel to handle recursive sub-structures.

Here is a part of Don's code, showing you how he uses some of the tricks described in this paper:


   # Output preformatted text.  This text must be surrounded by '<pre>' tags.
   # Since it can recursively contain other tags such as '<em>' or hyperlinks,
   # the procedure uses 'uplevel' on its final argument.
   proc cgi_preformatted {args} {
      cgi_put "<pre"
      cgi_close_proc_push "cgi_puts </pre>"

      if {[llength $args]} {
         cgi_put "[cgi_lrange $args 0 [expr [llength $args]-2]]"
      }
      cgi_puts ">"
      uplevel 1 [lindex $args end]
      cgi_close_proc
   }

   # Output a single list bullet.
   proc cgi_li {args} {
      cgi_put <li
      if {[llength $args] > 1} {
         cgi_put "[cgi_lrange $args 0 [expr [llength $args]-2]]"
      }
      cgi_puts ">[lindex $args end]"
   }

   # Output a bullet list.  It contains list bullets, represented
   # by calls to 'cgi_li' above.  Those calls are executed thanks
   # to 'uplevel'.
   proc cgi_bullet_list {args} {
      cgi_put "<ul"
      cgi_close_proc_push "cgi_puts </ul>"

      if {[llength $args] > 1} {
         cgi_put "[cgi_lrange $args 0 [expr [llength $args]-2]]"
      }
      cgi_puts ">"
      uplevel 1 [lindex $args end]

      cgi_close_proc
   }

I am not going to explain the fine details of this great library, but you can find out for yourself by downloading it from >> Don's homepage.




As another example, my TODL tool parses object-oriented schemas using parsing procedures such as class and method. Here is an example of an input file to the tool:


   # Todl schema for module 'shapes'.  It describes classes for some
   # geometrical shapes such as rectangles and squares.

   odl_module shapes {

      #######
      # Classes

      # Base class for all shapes.
      class shape {} {
         attr id 0   ; # Attribute 'id' is inherited by all shapes
                       # and has default value 0.
      }

      # Rectangle with a width and height.
      # Inherits from 'shape'.
      class rect {shape} {
         attr w 10
         attr h 10

         # Some methods to calculate properties for the shape,
         # and to draw it on the screen.
         method "" perimeter {}
         method "" area {}
         method "" draw { x {y 0} }
      }

      class square {shape} {
         ... (details similar to 'rect')
      }

      #######
      # Module parameters

      # All classes automatically get a 'print' method.
      param all { print }

      # Name of the 'delete' proc.
      param delete_name delete

      # We want debugging output:
      param debug 1
   }

By looking at this data file, can you find out what the complete list of all parsing procedures is?




I once wrote a (very!) simple parser for C++ class implementations. Lazy as I am, I wrote the parser in TCL. It actually turned out to be too complicated to be of any use, but it shows how far you can go with the Active File pattern. Just look at this data file containing twisted C++:


   class myListElt: public CListElt, private FString {
     This is a documentation string for the class 'myListElt'.
     You can see multiple inheritance here.
   } {

   public:
      method int GetLength(void) {
         This is a documentation string
         Returns the total length of the FString.
      } {
         // This is the final argument of the 'method' parsing proc.
         // It contains freeform text, so this is where I can write
         // pure C++ code, including the comment you are now reading.
         return myLength;
      }

      method char* GetString(void) {
         Returns the complete FString.
      } {
         append(0);
         return (char*)data;
      }

   private:
      method virtual void privateMethod(short int p1, short int p2) {
         Note that just saying "short" is not enough:
         you have to say "short int".
      } {
         printf("Boo!  p1=%d, p2=%d\n", p1, p2);
      }
   }

   data short int b {This is just a counter}
   data void* somePointer {to store the end-of-list or something like that}

   method void error(short int errNo, char* message) {
      This is a global library procedure, which
      reports an error message.
   } {
      cout << "Hey, there was an error (" << errNo << ") " << message << endl;
   }

   cpp_report

This example may be far-fetched, but it gives you an idea of the power of the Active File pattern. What you see is TCL code, but it looks a lot like C++ code, and it can automatically generate documentation, class diagrams, programming references and of course compilable C++ code.

The parsing procedures such as method and class store the C++ implementation in internal TCL data structures. Finally, the call to cpp_report generates the resulting C++ code.

The following fragment from the parser gives you an idea of how you can bend the TCL parser to read a file with C++-like syntax:

   # Parsing proc for 'class' keyword.
   # Arguments:
   # - class name
   # - list of inheritance specifications, optional
   # - comment block
   # - body block
   proc class {args} {
      global _cpp

      # split names from signs like : , *
      set cargs [expand [lrange $args 0 [expr [llength $args] - 3]]]
      # -3 to avoid the comment block and the class body.

      # First process the name
      set className [lindex $cargs 0]
      if { $_cpp(CL) == "" } {
         set _cpp(CL) $className   ; # This is like 'currPattern' in the
                                     # pattern repository example
      } else {
         error "Class definition for $className: we are already inside class $_cpp(CL)"
      }

      # Then process the inheritance arguments.
      # Obvisouly, this is already a lot more complicated than in the
      # previous examples.
      set inhr [list]
      set mode beforeColon
      set restArgs [lrange $cargs 1 end]
      foreach arg $restArgs {
         if { $arg == ":" } {
            if { $mode != "beforeColon" } {
               error "Misplaced \":\" in declaration \"class $className $restArgs\""
            }
            set mode afterColon
          } elseif { $arg == "public" || $arg == "private" } {
            if { $mode != "afterColon" } {
               error "Misplaced \"$arg\" in declaration \"class $className $restArgs\""
            }
            set mode $arg
          } elseif { $arg == "," } {
            if { $mode != "afterInherit" } {
               error "Misplaced \",\" in declaration \"class $className $restArgs\""
            }
            set mode afterColon
          } else {
            if { $mode != "public" &&  $mode != "private" } {
               error "Misplaced \"$arg\" in declaration \"class $className $restArgs\""
            }
            if { ![IsID $arg] } {
               warning "$arg is not a valid C++ identifier..."
            }
            lappend inhr [list $mode $arg]
            set mode afterInherit
         }
      }

      if { $mode != "afterInherit"  &&  $mode != "beforeColon" } {
         error "Missing something at end of declaration \"class $className $restArgs\""
      }

      set _cpp(CLih) $inhr
      set _cpp(CLac) "private"

      # First execute the comment block
      uplevel 1 [list syn_cpp_docClass [lindex $args [expr [llength $args] - 2]]]

      # Then execute the body
      uplevel 1 [list syn_cpp_bodyClass [lindex $args end]]

      set _cpp(CL) ""
      set _cpp(CLac) ""
      set _cpp(CLih) ""
   }



A word about being lazy

According to Perl's Larry Wall, one of the most important talents of a good programmer is lazyness. Creative lazyness, that is. This paper makes two suggestions that both come down to the same thing: be lazy.

"Reuse" is not all about encapsulation and information hiding. Sometimes it's just about being lazy.


Koen Van Damme (koen.vandamme1 at pandora.be)