| Type | Behavioral |
| Purpose | Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. |
Description
The Interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The pattern typically defines an expression abstraction whose implementations interpret elements of the language within a given context.
The pattern is named interpreter because it typically defines a language and implements an interpreter for that language, where the language is often a relatively simple domain-specific language, such as one for Boolean or arithmetic expressions.
It is classified under behavioral design patterns as it involves communication between objects.
Overview
| AbstractExpression | An interface or abstract class that declares an interpret operation that all nodes (terminal and nonterminal) in the abstract syntax tree implement. This operation takes a context object as a parameter which can be used to store the state of the interpreter. |
| TerminalExpression | Implements the AbstractExpression for symbol literals in the grammar. The Interpret method in the TerminalExpression implements the operations associated with the terminal symbols in the grammar, such as variables or constants. |
| NonterminalExpression | Also implements the AbstractExpression interface. NonterminalExpression represents grammar rules that include other rules. It typically implements its Interpret method by recursively calling the Interpret methods on the AbstractExpressions that it's composed of. |
| Context | Contains global information that is interpreted and modified as the interpreting process progresses. It's a means to share state across multiple expressions during the interpret. |
| Client | The client builds (or is provided with) the abstract syntax tree, represented as a specific hierarchy of AbstractExpression subclasses. The client initiates the interpretation by calling Interpret on the root node of the syntax tree. |
Implementation
// AbstractExpression
public interface IExpression
{
void Interpret(Context context);
}
public class Turtle : IExpression
{
public void Interpret(Context context)
{
Console.SetCursorPosition(context.X, context.Y);
Console.Write("*");
}
}
public class Move(int move) : IExpression
{
public void Interpret(Context context)
{
if (context.Direction == "EAST") context.X += move;
if (context.Direction == "WEST") context.X -= move;
if (context.Direction == "NORTH") context.Y -= move;
if (context.Direction == "SOUTH") context.Y += move;
}
}
public class Turn(string direction) : IExpression
{
public void Interpret(Context context)
=> context.Direction = direction.ToUpperInvariant();
}
// Context
public class Context
{
public int X { get; set; }
public int Y { get; set; }
public string Direction { get; set; } = "";
}
// Client
public static class InterpreterDemo
{
public static void Execute()
{
var context = new Context { X = 0, Y = 5, Direction = "EAST" };
List<IExpression> program =
[
new Move(5),
new Turtle(),
new Turn("NORTH"),
new Move(1),
new Turtle(),
new Move(2),
new Turtle(),
new Move(1),
new Turtle(),
new Turn("EAST"),
new Move(2),
new Turtle()
];
foreach (var expression in program)
{
expression.Interpret(context);
}
}
}
Summary
The Interpreter pattern isn't a recipe, but a general guideline. The roles — AbstractExpression, TerminalExpression, NonterminalExpression, Context, and Client — might not map exactly to these classes in all implementations of the pattern, especially in more complex or specialized cases.
For more information, please see here.