Previous Up

5  Design

This section describes the implementation of CamlTemplate; you don't need to read it unless you are interested in developing CamlTemplate itself.

CamlTemplate is a fairly straightforward implementation of the Interpreter1 pattern. It uses ocamllex and ocamlyacc to parse template source code, generating an abstract syntax tree consisting of objects; these objects do the work of interpreting the template.

5.1  The Abstract Syntax Tree

There are two kinds of objects in the abstract syntax tree, represented by the class type statement and the virtual class expression. Statements produce output; expressions have values. A template consists essentially of a list of statements (each of which may contain one or more lists of statements, e.g. to represent the body of a loop, or the branches of a conditional); when merged, the template iterates over its statements, calling each statement's interpret method in turn.

5.2  The Parser and Lexer

The parser is very straightforward, and probably needs no explanation if you are familiar with ocamlyacc. The lexer, on the other hand, is rather complicated, mainly because of the absence of delimiters around literal text in a template language; this requires us to assume that we are reading literal text until we get to something that looks like template language syntax.

The CamlTemplate lexer therefore maintains some state to indicate which sort of environment is being tokenised. The variable cur_mode keeps track of whether the lexer is currently in literal text, an expansion or a statement. For the most part, instead of using specialised rules, the lexer uses a single rule containing all the patterns that are meaningful in tokens; once it has matched a pattern, it decides what to do depending on its current mode.

5.3  Scopes

Scopes in CamlTemplate are roughly patterned after those in JavaScript. There are two writable scopes, template scope and macro scope; the template model is an additional read-only scope. Assignment and lookup of values in scopes are encapsulated in the scope class in ctScope.ml.

5.4  Thread Support

Since parser and lexer both maintain some global state, and since template caches are modifiable, they are all protected by a global mutex (in ctCache.ml) when thread support is linked in.


Previous Up