Skip to main content

Interpreter

Interpreter represents a simple language's grammar as a class hierarchy and evaluates sentences by walking an expression tree. It suits small, stable domain languages but scales poorly to complex grammars where parser tools are better.

Type
Behavioral
When to Use
Small Domain Language, Repeated Expression Evaluation, Simple Stable Grammar

Interpreter is a behavioral pattern that, given a language, defines a representation for its grammar along with an interpreter that uses the representation to evaluate sentences in the language. Each grammar rule becomes a class, and a sentence is represented as a tree of these objects, an abstract syntax tree. Interpreting a sentence means walking that tree and evaluating each node.

How It Works

The grammar is mapped to a class hierarchy. Terminal expressions represent the atomic symbols of the language, such as literals or variables. Nonterminal expressions represent rules that combine other expressions, such as addition or logical AND, and hold references to their sub-expressions. Each expression class implements an interpret(context) method. The context carries information the interpreter needs, like variable bindings. Parsing produces the tree; calling interpret on the root recursively evaluates the whole expression.

Because the tree is a Composite, the same interpret call works uniformly on leaves and composite nodes, and the recursion mirrors the grammar's structure.

When to Use It

Use Interpreter when you have a simple language to interpret and you can represent its statements as an abstract syntax tree, when the grammar is small and relatively stable, and when efficiency is not the top concern. It fits domain-specific rule engines, query filters, configuration mini-languages, regular-expression-like matchers, and arithmetic or boolean expression evaluation.

Trade-offs

The pattern works only for simple grammars; as a grammar grows, the number of classes explodes and maintenance becomes painful, at which point a parser generator or a proper interpreter architecture is better. Tree-walking interpretation is slow compared to compiled or bytecode approaches. The pattern also says nothing about parsing the input into the tree; you must supply that separately. For anything beyond a small, stable language, prefer dedicated parsing tools.

Related Patterns

Composite is the structural backbone of the expression tree, since terminal and nonterminal expressions share a uniform interface. Visitor is frequently used to add operations like pretty-printing, optimization, or compilation over the tree without bloating each expression class. Iterator can traverse the tree. Flyweight can share terminal expressions that recur many times. For anything beyond a small, stable grammar, larger language work moves toward lexer and parser generators, parser-combinator libraries, or bytecode interpreters, all of which scale where the hand-built Interpreter pattern does not.

Example

A feature-flag system evaluates rules like country == "US" AND tier >= 2. Terminal expressions read country and tier from the evaluation context; nonterminal expressions implement comparison, AND, OR, and NOT, holding their operands as child expressions. A parser builds the tree once when the rule is saved, and each request simply calls interpret with the current user context, which keeps evaluation fast even though parsing is not. New operators are added as new expression classes without touching existing ones. The same shape powers search-query mini-languages, spreadsheet formula engines, access-control rule evaluators, and templating conditionals, all cases where end users or administrators author short expressions in a constrained domain language that the application must evaluate repeatedly against changing data.