Interpreter Design Pattern in PHP

1. Definition

The Interpreter design pattern provides a way to evaluate sentences in a language. It involves implementing an expression interface that tells how to interpret a particular context. The main idea is to have one class for each symbol (terminal or non-terminal) in a specialized language.

2. Problem Statement

Consider needing to build a system to evaluate mathematical expressions. Hardcoding the logic to parse and evaluate these expressions can become convoluted, especially when the language grows in complexity.

3. Solution

The Interpreter pattern introduces classes for every grammar rule or token in the language. These classes share a common interface and interpret their specifics based on the context given to them.

4. Real-World Use Cases

1. Regular expression evaluators.

2. Query language interpreters like SQL.

3. Compilers and syntax checkers for programming languages.

5. Implementation Steps

1. Define an abstract expression that declares an interpret operation.

2. For every rule/token in the grammar, derive a concrete class from the abstract expression class.

3. The context class contains the global information, which gets modified as individual expressions parse it.

6. Implementation in PHP

<?php
// AbstractExpression
abstract class Expression {
    abstract public function interpret(array $context): bool;
}
// TerminalExpression
class TerminalExpression extends Expression {
    private $data;
    public function __construct(string $data) {
        $this->data = $data;
    }
    public function interpret(array $context): bool {
        return in_array($this->data, $context);
    }
}
// OrExpression
class OrExpression extends Expression {
    private $expr1;
    private $expr2;
    public function __construct(Expression $expr1, Expression $expr2) {
        $this->expr1 = $expr1;
        $this->expr2 = $expr2;
    }
    public function interpret(array $context): bool {
        return $this->expr1->interpret($context) || $this->expr2->interpret($context);
    }
}
// Client Code
$context = ['Lions', 'Tigers', 'Bears'];
$terminal1 = new TerminalExpression('Lions');
$terminal2 = new TerminalExpression('Dragons');
$orExp = new OrExpression($terminal1, $terminal2);
echo $orExp->interpret($context) ? 'True' : 'False';
?>

Output:

True

Explanation:

1. Expression: This is an abstract class that declares the interpret operation.

2. TerminalExpression: Represents terminal symbols in the grammar. In our example, this checks if the data is in the context.

3. OrExpression: Non-terminal expression. It checks the logical OR between two expressions.

4. The client sets the context and creates TerminalExpressions. It then checks using the OrExpression.

This pattern often results in many small, fine-grained classes that capture the logic of the domain language piece by piece.

7. When to use?

Use the Interpreter pattern when:

1. There's a language to interpret, and you can represent statements in the language as abstract syntax trees.

2. The grammar of the language is simple.

3. Efficiency is not a primary concern (large abstract syntax trees might be heavy on resources).


Comments