Design Patterns in Ruby: Interpreter

A clear example of the Interpreter pattern

D. L. Jerome
2 min readJan 16, 2017

The intent

According to the GoF, the intent of the Interpreter pattern is to:

“Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.”

The example

The Interpreter pattern is used extensively. It is the underlying pattern behind programming languages of all stripes. It is also frequently used to build domain specific languages ( DSLs ). Sometimes, the problem you are solving requires a new language with which to solve it. What follows is an example, peppered with comments, designed to be explanatory.

The example showcases the two major phases of the interpreting process. Those two phases are:

  1. parsing the program text into an abstract syntax tree (AST)
  2. evaluating (interpreting) the AST using expressions

Abstract syntax trees are composed of terminal (leaf) and non-terminal (higher order concept) nodes. Likewise, interpretive expressions are separated into both terminal expressions and non-terminal expressions. These terms may be alien to you. The example below will hopefully uncloak them.

The main take away: the central power of the Interpreter pattern is that you can generate different ASTs to produce different behavior, instead of having to recombine the behavior in increasingly complex and varied ways.

Some Tidbits

Contexts

The ‘Context’ of an interpreter are those values which are injected into the AST during evaluation. It constitutes the external conditions against which the parsed expressions contained within the AST are meant to be evaluated.

Perfomance vs expressiveness

Given the fact that interpreters are, as a general rule, not very performant, it is important to remember that when applying the Interpreter pattern we are knowingly trading performance for flexibility, extensibility, and expressiveness within a given domain.

A sign that you may benefit from an interpreter

If you find yourself building an increasing array of small chunks of behavior and combining them in ever more complex and varied ways, you might benefit from a interpreter that can generate those combinations for you, relying on a new and more concise grammar.

One caveat is that interpreters are best created when there are clear boundaries around a given problem domain. Otherwise, it is difficult to corral and define the grammar and usage of a given interpreter.

ASTs and Composites

The Abstract Syntax Tree (AST) of the Interpreter pattern is an example of the Composite pattern.

KISS

Given its many moving parts, it it prudent to restrict the scope of an interpreter to only a few expressions. This is keeping with the maxim, widely shared amongst programmers: “Keep It Simple, Stupid”.

An underused pattern

The Interpreter pattern is, regrettably, very underused. Generally, interpreters are seen are esoteric and complex to many engineers. With a little bit of study, however, the beauty of creating simple interpreters for what are otherwise difficult and unwieldy problems will reveal itself.

Going deeper

The Interpreter pattern is one of many valuable design patterns. To learn more, check out the seminal work on Design Patterns: The GOF Design Pattern book.

--

--