Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
Real-world example
The halfling kids are learning basic math at school. They start from the very basics “1 + 1”, “4 - 2”, “5 + 5”, and so forth.
In plain words
Interpreter pattern interprets sentences in the desired language.
Wikipedia says
In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a specialized computer language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to evaluate (interpret) the sentence for a client.
Programmatic example
To be able to interpret basic math, we need a hierarchy of expressions. The basic abstraction for
it is the Expression
class.
1public abstract class Expression {
2
3 public abstract int interpret();
4
5 @Override
6 public abstract String toString();
7}
The simplest of the expressions is the NumberExpression
that contains only a single integer
number.
1public class NumberExpression extends Expression {
2
3 private final int number;
4
5 public NumberExpression(int number) {
6 this.number = number;
7 }
8
9 public NumberExpression(String s) {
10 this.number = Integer.parseInt(s);
11 }
12
13 @Override
14 public int interpret() {
15 return number;
16 }
17
18 @Override
19 public String toString() {
20 return "number";
21 }
22}
The more complex expressions are operations such as PlusExpression
, MinusExpression
, and
MultiplyExpression
. Here’s the first of them, the others are similar.
1public class PlusExpression extends Expression {
2
3 private final Expression leftExpression;
4 private final Expression rightExpression;
5
6 public PlusExpression(Expression leftExpression, Expression rightExpression) {
7 this.leftExpression = leftExpression;
8 this.rightExpression = rightExpression;
9 }
10
11 @Override
12 public int interpret() {
13 return leftExpression.interpret() + rightExpression.interpret();
14 }
15
16 @Override
17 public String toString() {
18 return "+";
19 }
20}
Now we are able to show the interpreter pattern in action parsing some simple math.
1 // the halfling kids are learning some basic math at school
2 // define the math string we want to parse
3 final var tokenString = "4 3 2 - 1 + *";
4
5 // the stack holds the parsed expressions
6 var stack = new Stack<Expression>();
7
8 // tokenize the string and go through them one by one
9 var tokenList = tokenString.split(" ");
10 for (var s : tokenList) {
11 if (isOperator(s)) {
12 // when an operator is encountered we expect that the numbers can be popped from the top of
13 // the stack
14 var rightExpression = stack.pop();
15 var leftExpression = stack.pop();
16 LOGGER.info("popped from stack left: {} right: {}",
17 leftExpression.interpret(), rightExpression.interpret());
18 var operator = getOperatorInstance(s, leftExpression, rightExpression);
19 LOGGER.info("operator: {}", operator);
20 var result = operator.interpret();
21 // the operation result is pushed on top of the stack
22 var resultExpression = new NumberExpression(result);
23 stack.push(resultExpression);
24 LOGGER.info("push result to stack: {}", resultExpression.interpret());
25 } else {
26 // numbers are pushed on top of the stack
27 var i = new NumberExpression(s);
28 stack.push(i);
29 LOGGER.info("push to stack: {}", i.interpret());
30 }
31 }
32 // in the end, the final result lies on top of the stack
33 LOGGER.info("result: {}", stack.pop().interpret());
Executing the program produces the following console output.
popped from stack left: 1 right: 1
operator: +
push result to stack: 2
popped from stack left: 4 right: 2
operator: *
push result to stack: 8
result: 8
Use the Interpreter pattern when there is a language to interpret, and you can represent statements in the language as abstract syntax trees. The Interpreter pattern works best when