Interpreter Design Pattern

Last Updated : 3 Jun, 2026

The Interpreter Design Pattern is a behavioral design pattern used to define and evaluate the grammar of a language. It represents language rules as classes and interprets expressions by combining smaller expressions into a tree-like structure.

  • Each grammar rule is represented by a class, using terminal expressions (basic elements) and non-terminal expressions (combined elements).
  • It is useful for parsing and evaluating structured expressions such as mathematical formulas, command languages, and simple query languages.

Example: A calculator application can use the Interpreter Pattern to evaluate expressions like 5 + 3 - 2 by representing numbers and operators as expression objects and interpreting the result.

Components

These components work together to parse, interpret, and evaluate expressions based on a defined grammar.

  • AbstractExpression: An abstract class or interface that declares the interpret() method. It provides a common interface for all expression classes.
  • TerminalExpression: Represents the basic elements (terminal symbols) of the language. These classes implement the interpretation logic for simple expressions.
  • NonterminalExpression: Represents composite expressions made up of multiple sub-expressions. It combines and coordinates the interpretation of child expressions to produce the final result.
  • Context: Stores global information required during interpretation, such as variables, data structures, or state information.
  • Client: Creates the Abstract Syntax Tree (AST) and starts the interpretation process by calling the interpret() method on the root expression.
  • Interpreter: Manages the interpretation process by handling the context, traversing the expression tree, and evaluating expressions according to the grammar rules.

Real-Life Example

Consider yourself visiting a foreign nation where you are not fluent in the local tongue. To properly interact with the natives in such a situation, you might require the support of an interpreter.

Here's how the Interpreter pattern relates to this situation

  • Language Grammar: Every spoken language has its own grammar and syntax, much like a programming language has its own rules.
  • Interpreter: In this scenario, the individual who acts as an intermediary for you and the locals is the interpreter. Both the local language (the target language) and your language (the input language) are understood by them.
  • Expressions: Your spoken sentences or phrases are like expressions in a programming language. They represent the information or instructions you want to convey to the locals.
  • Context: The situational or cultural background in which the communication occurs could be the context in this analogy. The interpreter can better grasp the conversation's details and complexities due to this context.
  • Translation Process: The interpreter listens to your spoken expressions, interprets their meaning, and then translates them into the local language. They may break down your sentences into smaller units (words or phrases), understand their meaning, and then rephrase them in the target language using the appropriate grammar and vocabulary.

Benefits of using the Interpreter Pattern

The Interpreter Pattern improves the organization, flexibility, and maintainability of language-processing applications.

  • Modularity: Components such as terminal and non-terminal expressions can be easily added or modified to support new language constructs or operations.
  • Separation of Concerns: The pattern separates the grammar interpretation from the client, allowing the client to focus on providing input expressions while leaving the interpretation logic to the interpreter components.

InterpreterDesignPatternClassDiagram-(2)

Implementation Example

Problem Statement:

Suppose we have a simple language that supports basic arithmetic operations, such as addition (+), subtraction (-), multiplication (*), and division (/). We want to create a calculator program that can interpret and evaluate arithmetic expressions written in this language.

Communication flow of the Interpreter Design pattern using expression " 2+3*4 ":

The interpreter processes the expression by building and evaluating an expression tree step by step.

  • Client Input: Client passes the expression (2 + 3 × 4) to the interpreter.
  • Parsing & Tree Creation: Expression is converted into a tree with terminal (2, 3, 4) and non-terminal (+, ×) nodes.
  • Evaluation & Result: Interpreter evaluates 3 × 4 = 12, then 2 + 12 = 14, and returns 14 to the client.

Let's break down into the component wise code:

1. Client

The client provides input expressions and interacts with the interpreter.

C++
#include <iostream>

class Expression {
public:
    virtual int interpret() = 0;
};

class NumberExpression : public Expression {
private:
    int number;
public:
    NumberExpression(int number) : number(number) {}
    int interpret() override {
        return number;
    }
};

class AdditionExpression : public Expression {
private:
    Expression* left;
    Expression* right;
public:
    AdditionExpression(Expression* left, Expression* right) : left(left), right(right) {}
    int interpret() override {
        return left->interpret() + right->interpret();
    }
};

class MultiplicationExpression : public Expression {
private:
    Expression* left;
    Expression* right;
public:
    MultiplicationExpression(Expression* left, Expression* right) : left(left), right(right) {}
    int interpret() override {
        return left->interpret() * right->interpret();
    }
};

int main() {
    // Manually building AST for 2 + 3 * 4
    Expression* expression = new AdditionExpression(
        new NumberExpression(2),
        new MultiplicationExpression(
            new NumberExpression(3),
            new NumberExpression(4)
        )
    );

    int result = expression->interpret();
    std::cout << "Result: " << result << std::endl;
    return 0;
}
Java
public class Client {
    public static void main(String[] args) {

        // Manually building AST for 2 + 3 * 4
        Expression expression = new AdditionExpression(
            new NumberExpression(2),
            new MultiplicationExpression(
                new NumberExpression(3),
                new NumberExpression(4)
            )
        );

        int result = expression.interpret();
        System.out.println("Result: " + result);
    }
}
Python
class Expression:
    def interpret(self):
        pass

class NumberExpression(Expression):
    def __init__(self, number):
        self.number = number
    def interpret(self):
        return self.number

class AdditionExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def interpret(self):
        return self.left.interpret() + self.right.interpret()

class MultiplicationExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def interpret(self):
        return self.left.interpret() * self.right.interpret()

# Manually building AST for 2 + 3 * 4
expression = AdditionExpression(
    NumberExpression(2),
    MultiplicationExpression(
        NumberExpression(3),
        NumberExpression(4)
    )
)

result = expression.interpret()
print("Result:", result)
JavaScript
class Expression {
    interpret() {}
}

class NumberExpression extends Expression {
    constructor(number) {
        super();
        this.number = number;
    }
    interpret() {
        return this.number;
    }
}

class AdditionExpression extends Expression {
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }
    interpret() {
        return this.left.interpret() + this.right.interpret();
    }
}

class MultiplicationExpression extends Expression {
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }
    interpret() {
        return this.left.interpret() * this.right.interpret();
    }
}

// Manually building AST for 2 + 3 * 4
const expression = new AdditionExpression(
    new NumberExpression(2),
    new MultiplicationExpression(
        new NumberExpression(3),
        new NumberExpression(4)
    )
);

const result = expression.interpret();
console.log('Result:', result);

2. Abstract Expression

Defines the common interface for interpreting expressions.

C++
#include <iostream>

class Expression {
public:
    virtual int interpret() = 0;
};
Java
public interface Expression {
    int interpret();
}
Python
from abc import ABC, abstractmethod

class Expression(ABC):
    @abstractmethod
    def interpret(self):
        pass
JavaScript
class Expression {
    interpret() {
        throw new Error('Method interpret() must be implemented.');
    }
}

3. Terminal Expression

Represents basic language elements.

C++
#include <iostream>
class NumberExpression {
private:
    int number;
public:
    NumberExpression(int number) : number(number) {}
    int interpret() {
        return number;
    }
};
Java
public class NumberExpression implements Expression {
    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
}
Python
class NumberExpression:
    def __init__(self, number):
        self.number = number
    def interpret(self):
        return self.number
JavaScript
class NumberExpression {
    constructor(number) {
        this.number = number;
    }
    interpret() {
        return this.number;
    }
}

4. Non-Terminal Expression

Represents composite language constructs.

C++
#include <iostream>
using namespace std;

class Expression {
public:
    virtual int interpret() = 0;
};

class AdditionExpression : public Expression {
private:
    Expression* left;
    Expression* right;

public:
    AdditionExpression(Expression* left, Expression* right) : left(left), right(right) {}
    int interpret() override {
        return left->interpret() + right->interpret();
    }
};

class MultiplicationExpression : public Expression {
private:
    Expression* left;
    Expression* right;

public:
    MultiplicationExpression(Expression* left, Expression* right) : left(left), right(right) {}
    int interpret() override {
        return left->interpret() * right->interpret();
    }
};
Java
public class AdditionExpression implements Expression {
    private Expression left;
    private Expression right;

    public AdditionExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

public class MultiplicationExpression implements Expression {
    private Expression left;
    private Expression right;

    public MultiplicationExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() * right.interpret();
    }
}
Python
from abc import ABC, abstractmethod

class Expression(ABC):
    @abstractmethod
    def interpret(self):
        pass

class AdditionExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def interpret(self):
        return self.left.interpret() + self.right.interpret()

class MultiplicationExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def interpret(self):
        return self.left.interpret() * self.right.interpret()
JavaScript
class Expression {
    interpret() {
        throw new Error('Method interpret() must be implemented.');
    }
}

class AdditionExpression extends Expression {
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }
    interpret() {
        return this.left.interpret() + this.right.interpret();
    }
}

class MultiplicationExpression extends Expression {
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }
    interpret() {
        return this.left.interpret() * this.right.interpret();
    }
}

Complete code for the above example

Tomplete code for the above example is

C++
#include <iostream>

class Expression {
public:
    virtual int interpret() = 0;
};

class NumberExpression : public Expression {
private:
    int number;
public:
    NumberExpression(int number) : number(number) {}
    int interpret() override {
        return number;
    }
};

class AdditionExpression : public Expression {
private:
    Expression* left;
    Expression* right;
public:
    AdditionExpression(Expression* left, Expression* right) : left(left), right(right) {}
    int interpret() override {
        return left->interpret() + right->interpret();
    }
};

class MultiplicationExpression : public Expression {
private:
    Expression* left;
    Expression* right;
public:
    MultiplicationExpression(Expression* left, Expression* right) : left(left), right(right) {}
    int interpret() override {
        return left->interpret() * right->interpret();
    }
};

int main() {
    // Manually building AST for 2 + 3 * 4
    Expression* expression = new AdditionExpression(
        new NumberExpression(2),
        new MultiplicationExpression(
            new NumberExpression(3),
            new NumberExpression(4)
        )
    );

    int result = expression->interpret();
    std::cout << "Result: " << result << std::endl;
    return 0;
}
Java
interface Expression {
    int interpret();
}

class NumberExpression implements Expression {
    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
}

class AdditionExpression implements Expression {
    private Expression left;
    private Expression right;

    public AdditionExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

class MultiplicationExpression implements Expression {
    private Expression left;
    private Expression right;

    public MultiplicationExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() * right.interpret();
    }
}

public class Client {
    public static void main(String[] args) {

        // Manually building AST for 2 + 3 * 4
        Expression expression = new AdditionExpression(
            new NumberExpression(2),
            new MultiplicationExpression(
                new NumberExpression(3),
                new NumberExpression(4)
            )
        );

        int result = expression.interpret();
        System.out.println("Result: " + result);
    }
}
Python
from abc import ABC, abstractmethod


class Expression(ABC):
    @abstractmethod
    def interpret(self):
        pass


class NumberExpression(Expression):
    def __init__(self, number):
        self.number = number

    def interpret(self):
        return self.number


class AdditionExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def interpret(self):
        return self.left.interpret() + self.right.interpret()


class MultiplicationExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def interpret(self):
        return self.left.interpret() * self.right.interpret()


if __name__ == "__main__":
    # Manually building AST for 2 + 3 * 4
    expression = AdditionExpression(
        NumberExpression(2),
        MultiplicationExpression(
            NumberExpression(3),
            NumberExpression(4)
        )
    )

    result = expression.interpret()
    print(f"Result: {result}")
JavaScript
class Expression {
    interpret() {
        throw new Error('Method not implemented.');
    }
}

class NumberExpression extends Expression {
    constructor(number) {
        super();
        this.number = number;
    }

    interpret() {
        return this.number;
    }
}

class AdditionExpression extends Expression {
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }

    interpret() {
        return this.left.interpret() + this.right.interpret();
    }
}

class MultiplicationExpression extends Expression {
    constructor(left, right) {
        super();
        this.left = left;
        this.right = right;
    }

    interpret() {
        return this.left.interpret() * this.right.interpret();
    }
}

// Manually building AST for 2 + 3 * 4
const expression = new AdditionExpression(
    new NumberExpression(2),
    new MultiplicationExpression(
        new NumberExpression(3),
        new NumberExpression(4)
    )
);

const result = expression.interpret();
console.log(`Result: ${result}`);

Output
Result: 14

When to use

Use this pattern when you need to define and evaluate structured language grammar in a flexible way.

  • Domain-Specific Language (DSL): Use it when you are creating a small custom language with its own rules and commands. It helps you define and execute those rules clearly.
  • Well-Defined Grammar: Use it when expressions follow fixed grammar rules that need to be parsed and evaluated step by step. It makes handling structured input easier.
  • Frequent New Operations: Use it when you often need to add new commands or operations. You can extend the system without changing existing code.
  • Avoid Complex Parsers: Use it when building a full compiler or parser feels too complex for your needs. It provides a simpler way to interpret expressions.

When not to use

Avoid this pattern when it adds unnecessary complexity or performance overhead.

  • For Simple Computations: If built-in language features or libraries can handle the task easily, using the Interpreter Pattern may add unnecessary complexity.
  • When Performance is Critical: Interpreting expressions can introduce overhead, making it less suitable for high-performance applications.
  • When the Grammar is Too Complex: A large and complex grammar can result in too many expression classes, increasing code complexity.
  • When Extensibility is Not Needed: If the application's requirements are fixed and unlikely to change, the Interpreter Pattern may be unnecessary.
Comment

Explore