Interpreter Pattern
The Interpreter Pattern provides a way to evaluate language grammar or expressions. It is used to define a representation of grammar along with an interpreter that uses the representation to interpret sentences in the language.
🧩 What is the Interpreter Pattern?
The Interpreter Pattern is a behavioral design pattern that specifies how to evaluate sentences in a language. It defines a grammar for a simple language and uses an interpreter to process statements of that language.
🎯 Real-World Analogy
Think of a calculator parsing a mathematical expression like "2 + 3 - 1"
. Each number and operation has meaning, and the system understands how to compute it by interpreting each token based on grammar rules.
📊 UML Diagram
classDiagram class AbstractExpression { <<interface>> + interpret(context) } class TerminalExpression { + interpret(context) } class NonTerminalExpression { + interpret(context) } class Context { + input + output } AbstractExpression <|-- TerminalExpression AbstractExpression <|-- NonTerminalExpression
🏗️ Structure and Participants
- Context: Holds the information that is global to the interpreter. This typically includes the input string or data to be parsed and any output or state needed during interpretation.
-
AbstractExpression:
Defines the common interface (usually an
interpret(context)
method) for all concrete expression classes. Every grammar rule or symbol will implement this interface. - TerminalExpression: Implements the interpretation for grammar elements that do not contain other expressions (i.e., leaf nodes in the grammar tree). For example, numbers or variables in a mathematical expression.
- NonTerminalExpression: Implements grammar rules that involve other expressions (i.e., composite nodes). These classes combine terminal and/or non-terminal expressions to represent more complex grammar constructs, such as addition or subtraction in an arithmetic language.
💡 Use Cases
- Simple language interpreters (arithmetic, boolean)
- Rule engines or DSLs (Domain-Specific Languages)
- Command parsing in chatbots or command-line interfaces
- Validation engines for expressions or conditions
👨💻 Example Code
// Abstract Expression
interface IExpression {
int Interpret();
}
// Terminal
class Number : IExpression {
private int _value;
public Number(int value) => _value = value;
public int Interpret() => _value;
}
// Non-Terminal
class Add : IExpression {
private IExpression _left, _right;
public Add(IExpression left, IExpression right) {
_left = left;
_right = right;
}
public int Interpret() => _left.Interpret() + _right.Interpret();
}
// Usage
var expression = new Add(new Number(5), new Number(3));
Console.WriteLine(expression.Interpret()); // Output: 8
from abc import ABC, abstractmethod
class Expression(ABC):
@abstractmethod
def interpret(self):
pass
class Number(Expression):
def __init__(self, value):
self.value = value
def interpret(self):
return self.value
class Add(Expression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self):
return self.left.interpret() + self.right.interpret()
# Usage
expr = Add(Number(5), Number(3))
print(expr.interpret()) # Output: 8
⚖️ Pros and Cons
Pros | Cons |
---|---|
Good for simple grammar parsing and expression evaluation | Can become complex and hard to maintain for large or evolving grammars |
Extensible—easy to add new grammar rules or expressions by introducing new classes | Poor performance for complex expressions due to recursive interpretation and object creation |
Improves readability and separation of concerns by encapsulating grammar logic | Class explosion: each grammar rule or symbol often requires a new class, leading to many small classes |
Useful for implementing DSLs, rule engines, and interpreters for custom languages | Not suitable for parsing complex languages—better tools exist (e.g., parser generators, ANTLR) |
Promotes reusability and testability of grammar components | Can be difficult to debug and optimize as the grammar grows |
📌 When to Use
- You have a simple grammar to interpret.
- You want to implement a small language, query, or expression evaluator.
- You want a highly extensible solution where new rules are frequently added.
🧠 Quiz Time
📝 Summary
The Interpreter Pattern is powerful when used in the right scenarios, such as custom language parsers and expression evaluation engines. However, for complex grammars, it’s often better to use tools like ANTLR or parser generators.