The C++ Interpreter Pattern for Grammar Management
What’s in a Grammar?
For me, the word grammar always conjures up memories of computing courses about compiler design or distant remembrances of school lessons on clauses, adjectives, and nouns. Few words carry so much baggage! The role of grammar in programming is crucial—imagine trying to write C++ or Java without knowing the grammar (rules) of the language.
There is a growing role for "generic" grammars that are used to accomplish specialized tasks. Aspect-oriented programming (AOP) is an example, where software operation can be modified based on simple scripts (grammars). Another, rather more abstract example is the growing requirement for business IT to be driven on the basis of rules. Rather than having IT dictate how it should be used, there is now a need for business facilitation by the IT infrastructure. In other words, IT is becoming a utility—similar to power, water, and so on.
This all sounds rather abstract and remote, but the key point is that simple grammars will increasingly be used to achieve rule-driven IT scenarios. In this article, I describe how a simple grammar can be modeled using the interpreter design pattern. I’ll then show how this could be extended to handle more complex requirements, such as those mentioned above.
As usual, a sneak preview of the final code is illustrated in Listing 1.
Listing 1 A Boolean grammar in C++.
VariableExpression* x = new VariableExpression("X"); VariableExpression* y = new VariableExpression("Y"); expression = new OrExpression( new AndExpression (new Constant(true), x), new AndExpression (y, new NotExpression(x))); context.AssignX(x, true); context.AssignY(y, false); bool result = expression->Evaluate(context);
Listing 1 illustrates a classic use of the interpreter design pattern to implement a simple Boolean grammar. Let’s look a little more closely at this approach.