I often see references to the interpreter design pattern in papers related to
programming language design. This short post is here to help me remember what
this pattern reference usually means, as well as document its relation to the
composite design pattern.

The syntax tree of a sentence in the language is an instance of the
composite pattern and is used to evaluate (interpret) the sentence for a
client.

As a compiler hacker, all of this sounds very familiar. Indeed, if you've ever
written an interpreter or compiler for a programming language or a
domain-specific language - even a simple one - you've almost certainly used both
the interpreter and composite patterns.

Suppose we have a very simple language for evaluating mathematical expressions,
and we want to write an interpreter for it. Using the classical compiler
work-flow we'll tokenize the language, parse it to produce a syntax tree and
then either interpret this tree directly or compile it down to some
lower-level representation. For the purpose of this post, we'll assume:

Direct evaluation (interpretation) on the tree is used. A compiler would use
exactly the same pattern, except that it would emit some sort of code instead
of direct results.

We don't care about how the tree is constructed, i.e. the syntax of the
language. This post's code sample starts with the constructed syntax tree in
memory and focuses on how it's represented and interpreted.

With this in mind, here's a simple C++ program that represents expressions and
evaluates them. I'll show the code piecemeal to explain what it does; the full
code sample is available here.

We'll start with an abstract interface called Expr which all syntax elements
have to implement:

// Maps symbol names to their values. An expression is evaluated in the context// of a symbol table, in order to assign concrete values to variables referenced// within it.typedefstd::map<std::string,double>SymbolTable;// Abstract interface for expressions in the language.classExpr{public:// Evaluate the expression in the context of the given symbol table, which// is to be used to resolve (or update) variable references.virtualdoubleEval(SymbolTable*st)const=0;};

Expressions such as constants and variable references are often called
terminal, or leaf expressions, since they don't contain other expressions
within them. Let's add a more complex, non-leaf expression:

// A function type for computing the result of a binary operation.typedefstd::function<double(double,double)>BinaryFunction;classBinaryOp:publicExpr{public:BinaryOp(BinaryFunctionfunc,constExpr&lhs,constExpr&rhs):func_(func),lhs_(lhs),rhs_(rhs){}doubleEval(SymbolTable*st)const{returnfunc_(lhs_.Eval(st),rhs_.Eval(st));}private:BinaryFunctionfunc_;constExpr&lhs_;constExpr&rhs_;};

Note how BinaryOp implements the same interface as the leaf expressions. Its
Eval defers to the Eval method of its constituent left-hand-side and
right-hand-side expressions. This is an embodiment of the Composite design
pattern, defined as:

[...] describes that a group of objects is to be treated in the same way as
a single instance of an object. The intent of a composite is to "compose"
objects into tree structures to represent part-whole hierarchies.
Implementing the composite pattern lets clients treat individual objects and
compositions uniformly.

In the language of the Composite pattern, there are leaf and composite
classes, both of which are components. In our example, a Constant is a
leaf, and so is a VarRef. A BinaryOp is a composite. Both inherit from
Expr, which is the component.

The core of the composite pattern manifests here in the uniform interface
(Expr) implemented by both Constant ("individial object" in the
definition quoted above) and BinaryOp ("composition").

I'm not a big fan of UML, but since this is design patterns we're talking about,
I couldn't help myself ;-) Here's our class diagram described in UML. Note the
close conceptual resemblance to the UML diagram on the Composite Pattern
Wikipedia page.

Finally, let us see these classes in action. Here's a main function that
hand-assembles a simple expression and evaluates it. This is a toy for
demonstration purposes; in a real program, the syntax tree would be built
automatically, most likely by a parser.

intmain(intargc,constchar**argv){// Define a couple of constants and a reference to the variable 'A'.std::unique_ptr<Expr>c1(newConstant(2.0));std::unique_ptr<Expr>c2(newConstant(3.3));std::unique_ptr<Expr>v(newVarRef("A"));// Define a binary expression representing "2.0 * 3.3 + A"std::unique_ptr<Expr>e1(newBinaryOp(std::multiplies<double>(),*c1,*c2));std::unique_ptr<Expr>e2(newBinaryOp(std::plus<double>(),*e1,*v));// Evaluate in the context of a symbol table where A has the value 1.1SymbolTablest{{"A",1.1}};std::cout<<e2->Eval(&st)<<"\n";return0;}

The expression tree created by this code is:

It is then evaluated with the context of A = 1.1, and the result is 7.7, as
expected.

Finally, I'll mention that while this example is very typical of a scenario in
which I usually encounter these two patterns, it's by no means the only one.

The Composite pattern has life outside interpreters, of course. It's useful
whenever a group of objects can be handled in a uniform manner as a single
object. For example, in the world of graphics we may have shape objects that can
be moved, rotated, and so on; we may want to treat a "group of shapes" similarly
(move all shapes within it equally, rotate the group, etc). This calls for the
use of the composite pattern where all shapes, as well as a "shape group" derive
from a common component interface.

The Interpreter pattern is useful whenever a problem can be described by a
language of any sort. Some examples are SQL or other logical query methods,
regular expressions, many kinds of rule-based systems, etc.