[Chapter 17] 17.3 Jeeves Overview
Author: Sriram Srinivasan
Date: 1999-07-02T01:39:47Z

#

| Chapter 17 Template-Driven Code Generation |

## 17.3 Jeeves Overview

Figure 17.2 illustrates how various components of a Jeeves-based translator relate to each other. Gray rectangles constitute the Jeeves framework.

#### Figure 17.2: Components of a running Jeeves-based translator

The Jeeves framework supplies a driver program, `jeeves`, a template-parsing module, TemplateParser.pm, and a utility module for creating and accessing the syntax tree, AST.pm.

You supply a specification parser for a given application domain, such as OO_Schema.pm, a specification file (emp.om), and one or more templates, such as oo.tpl.

The driver starts by calling the `parse` function of the specification parser. This calls the AST functions `new`, `add_prop`, and `add_prop_list` to convert all "relevant" data from the specification file to a tree of properties.

The driver then calls the template parser's `parse` function, which converts the given template file to an intermediate perl file. (Note the line `Translated` `oo.tpl` `to oo.tpl.pl` in the command-line invocation shown earlier.) The template contains variables along with looping and conditional constructs, all of which are more than adequately supported by Perl itself, so by converting the template to Perl code, we are able to leverage all of Perl's power. This is similar to early versions of C++ compilers (`cfront`), which simply converted C++ files to intermediate C files, thus taking advantage of the power, optimization features, and portability of existing C compilers.

Finally, the driver loads the intermediate file using `require`, which is an `eval` in disguise. When evaluated, this code traverses the syntax tree and produces the required output files.

### 17.3.1 Advantages of This Architecture

So what have we gained by this seemingly complicated arrangement? The input parser is written once to produce a standardized data structure. The template parser knows how to make this data structure available to the template and traverse it in a controlled fashion. The result is that you can write all kinds of templates while reusing the input parser.

This arrangement works very well in big projects. Someone with parsing experience writes the parser, and another who knows the application well writes templates. Others simply write different specifications and run the tool. Tomorrow, if you, an applications developer, write a template to automatically generate a Motif-based user interface from an object model, the others don't have to learn Motif to generate custom UIs for their object models.

The `jeeves` driver takes the name of a specification parser on the command line. This means that you can have a library of specification parsers for all kinds of problems and a library of templates corresponding to these parsers. The framework itself is independent of application domains.

The advantage of writing it in Perl is that no other language comes anywhere close to Perl's text-processing abilities. Besides, you can use modules such as Tk and IO::Socket within your templates.

### 17.3.2 Intermediate Perl Code

Most of Jeeves is extremely simple; the only piece of code that warrants some attention is the template parser.

The following snippet shows a sample conversion of a template to the intermediate Perl file:

    @foreach class_list
    Name: $class_name
      @foreach attr_list
      Attr: $attr_name, $attr_type
      @end
    @end

Stripped to its essence, the corresponding intermediate file looks like this:

    $ROOT->visit();
    foreach $class_list_i (@class_list) {
        $class_list_i->visit();
        print "Name : $class_name\n";
        foreach $attr_list_i (@attr_list) {
             $attr_list_i->visit();
             print "Attr: $attr_name, $attr_type\n";
             Ast->bye();
        }
        Ast->bye();
    }

Ast::visit converts all properties of the AST node being visited to global Perl variables in package `main`. The root node of the syntax tree is "visited" first, which results in the creation of one global variable, `@class_list`, since that is the sole property of the root node. `@class_list` consists of one or more AST nodes itself, and when one of them is visited, the properties class_name and attr_list become available as `$class_name` and `@attr_list`. This code has to account for the possibility that a given global variable already exists, either because of a similarly named property at an outer level of nesting or because one was defined by the template writer using an `@perl` directive. For this reason `visit()` keeps track of the old value of a variable if necessary; `bye()` restores it to the old value at the end of a `@FOREACH` block. This arrangement hence implements dynamic scoping.

17.2 Jeeves Example |  | 17.4 Jeeves Implementation