DEV Community

Binoy Vijayan
Binoy Vijayan

Posted on • Edited on

GoF-Interpreter Pattern

The Interpreter Pattern is a behavioural design pattern that defines a way to evaluate language grammar or expressions. It provides a mechanism to interpret or parse sentences in a language, represented as a set of objects or expressions. This pattern is particularly useful when dealing with problems that can be easily represented in a grammar and when efficiency is not a primary concern.

Structure :

AbstractExpression: Defines an abstract interface for interpreting expressions. Typically, each grammar rule in the language corresponds to a subclass of this interface.

TerminalExpression: Represents terminal symbols in the grammar. These are the basic building blocks of the language. Terminal expressions implement the interpretation operation directly.

NonterminalExpression: Represents non-terminal symbols in the grammar, which are combinations of one or more terminal or non-terminal symbols. Non-terminal expressions interpret complex expressions by delegating interpretation to their subexpressions.

Context: Contains any necessary global information that the interpreter may need to interpret expressions. It provides the context for interpretation.

Client: Builds the abstract syntax tree representing a particular sentence in the language and initiates the interpretation process.

Workflow:

Construction of Abstract Syntax Tree (AST): The client builds an abstract syntax tree representing the structure of the sentence or expression to be interpreted. Each node in the tree represents a construct in the language, such as a terminal or non-terminal expression.

Interpretation: The client initiates the interpretation process by calling the interpret() method on the root node of the abstract syntax tree. The interpretation process recursively traverses the tree, evaluating each node based on its type and context.

Example Use Cases

Query Languages Interpreting queries in databases or search engines.

Rule-Based Systems Implementing rule engines or expert systems where rules are defined in a formal language.

Mathematical Expressions

Evaluating mathematical expressions in calculator applications or mathematical software.

Real world example

An example of the Interpreter pattern in a common person's life can be found in the context of navigating and understanding street signs while driving.

Here's how the Interpreter pattern applies

AbstractExpression: This represents the abstract class or protocol defining the interpret(context:) method. In the example, it could be represented by the abstract notion of interpreting traffic symbols and signals.

TerminalExpression: These are the basic expressions that cannot be further decomposed. In the example, street signs and traffic signals serve as terminal expressions.

NonterminalExpression: These are composite expressions formed by combining multiple sub-expressions. In the example, driving actions or pedestrian actions based on the interpretation of street signs and traffic signals could be considered non-terminal expressions.

Context: The environment or context in which the expressions are evaluated. In the example, the road network and traffic regulations form the context.

Client: This is the entity that uses the interpreter pattern to interpret expressions in the context. In the example, drivers and pedestrians act as clients, interpreting street signs and traffic signals to navigate safely.

Here we are trying to translate the same into Swift code

// AbstractExpression
protocol TrafficInterpreter {
    func interpret(_ context: RoadContext) -> String
}

// TerminalExpression
class StreetSign: TrafficInterpreter {
    private let message: String

    init(message: String) { self.message = message }

    func interpret(_ context: RoadContext) -> String {
        return "Interpreting street sign: \(message)"
    }
}

class TrafficSignal: TrafficInterpreter {
    private let color: String

    init(color: String) { self.color = color }

    func interpret(_ context: RoadContext) -> String {
        return "Interpreting traffic signal: \(color)"
    }
}

// NonterminalExpression
class DrivingAction: TrafficInterpreter {
    private let action: String

    init(action: String) { self.action = action }

    func interpret(_ context: RoadContext) -> String {
        return "Taking driving action: \(action)"
    }
}

class PedestrianAction: TrafficInterpreter {
    private let action: String

    init(action: String) { self.action = action }

    func interpret(_ context: RoadContext) -> String {
        return "Taking pedestrian action: \(action)"
    }
}


// Context
class RoadContext {
    /* Additional context information if needed */
}

// Client
class Driver {
    private let roadContext: RoadContext

    init(roadContext: RoadContext) { self.roadContext = roadContext }

    func interpret(_ expression: TrafficInterpreter) -> String {
        return expression.interpret(roadContext)
    }
}

class Pedestrian {
    private let roadContext: RoadContext

    init(roadContext: RoadContext) {
        self.roadContext = roadContext
    }

    func interpret(_ expression: TrafficInterpreter) -> String {
        return expression.interpret(roadContext)
    }
}

// Example Usage
let roadContext = RoadContext()

let driver = Driver(roadContext: roadContext)
let pedestrian = Pedestrian(roadContext: roadContext)

let streetSign = StreetSign(message: "Speed Limit 40")
let trafficSignal = TrafficSignal(color: "Red")

print(driver.interpret(streetSign)) // Output: Interpreting street sign: Speed Limit 40
print(pedestrian.interpret(trafficSignal)) // Output: Interpreting traffic signal: Red
Enter fullscreen mode Exit fullscreen mode

Image description

In this diagram:

TrafficInterpreter is the abstract expression defining the interpret(context:) method.

StreetSign, TrafficSignal, DrivingAction, and PedestrianAction are concrete implementations of TrafficInterpreter, representing various expressions.

RoadContext represents the context in which the expressions are interpreted.

Conclusion:

The Interpreter Pattern provides a powerful mechanism for interpreting and evaluating expressions defined in a language grammar. By representing expressions as objects and separating interpretation logic, it simplifies complex expression evaluation tasks. Understanding and applying this pattern can lead to more flexible, maintainable, and reusable code in applications involving expression parsing and evaluation.

Overview of GoF Design Patterns

Top comments (0)