# Interpreter

- Purpose
- Structure
- Applications
- Consequences

## Purpose

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

## Structure

- AbstractExpression(RegularExpression): declares an abstract Interpret operation that is common to all nodes in the abstract syntax
- TerminalExpression(LiteralExpression): implements an Interpret operation associated with terminal symbols in the grammar. an instance is required for every terminal symbol in a sentence.
- NonterminalExpression(AlternationExpression, RepetitionExpression, SequenceExpressions): one such class is required for every rule R ::= R1 R2 ... Rn in the grammar. maintains instance variables of type AbstractExpression for each of the symbols R1 through Rn. implements an Interpret operation for nonterminal symbols in the grammar. Interpret typically calls itself recursively on the variables representing R1 through R.
- Context: contains information that's global to the interpreter.
- Client: builds (or is given) an abstract syntax tree representing a particular sentence in the language that the grammar defines. The abstract syntax tree is assembled from instances of
  the NonterminalExpression and TerminalExpression classes. invokes the Interpret operation.

## Applications

- l the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time.
- efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example,
  regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable.

## Consequences

- It's easy to change and extend the grammar. Because the pattern uses classes to represent grammar rules, you can use inheritance to change or extend the grammar. Existing expressions
  can be modified incrementally, and new expressions can be defined as variations on old ones.
- Implementing the grammar is easy, too. Classes defining nodes in the abstract syntax tree have similar implementations. These classes are easy to write, and often their generation can be
  automated with a compiler or parser generator.
- Complex grammars are hard to maintain. The Interpreter pattern defines at least one class for every rule in the grammar (grammar rules defined using BNF may require multiple classes).
  Hence grammars containing many rules can be hard to manage and maintain. Other design patterns can be applied to mitigate the problem (see Implementation). But when the grammar is
  very complex, other techniques such as parser or compiler generators are more appropriate.
- Adding new ways to interpret expressions. The Interpreter pattern makes it easier to evaluate an expression in a new way. For example, you can support pretty printing or type-checking an expression by defining a new operation on the expression classes. If you keep creating new ways of interpreting an expression, then consider using the Visitor pattern to avoid changing the grammar classes.

กก