3.3. Interpreter

3.3.1. Purpose

For a given language, it defines the representation of its grammar as “No Terminal Expression” and “Terminal Expression”, as well as an interpreter for the sentences of that language.

3.3.2. Examples

  • An example of a binary logic interpreter, each definition is defined by its own class

3.3.3. UML Diagram

Alt Interpreter UML Diagram

3.3.4. Code

You can also find this code on GitHub

AbstractExp.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\Interpreter;
 6
 7abstract class AbstractExp
 8{
 9    abstract public function interpret(Context $context): bool;
10}

Context.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\Interpreter;
 6
 7use Exception;
 8
 9class Context
10{
11    private array $poolVariable;
12
13    public function lookUp(string $name): bool
14    {
15        if (!key_exists($name, $this->poolVariable)) {
16            throw new Exception("no exist variable: $name");
17        }
18
19        return $this->poolVariable[$name];
20    }
21
22    public function assign(VariableExp $variable, bool $val)
23    {
24        $this->poolVariable[$variable->getName()] = $val;
25    }
26}

VariableExp.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\Interpreter;
 6
 7/**
 8 * This TerminalExpression
 9 */
10class VariableExp extends AbstractExp
11{
12    public function __construct(private string $name)
13    {
14    }
15
16    public function interpret(Context $context): bool
17    {
18        return $context->lookUp($this->name);
19    }
20
21    public function getName(): string
22    {
23        return $this->name;
24    }
25}

AndExp.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\Interpreter;
 6
 7/**
 8 * This NoTerminalExpression
 9 */
10class AndExp extends AbstractExp
11{
12    public function __construct(private AbstractExp $first, private AbstractExp $second)
13    {
14    }
15
16    public function interpret(Context $context): bool
17    {
18        return $this->first->interpret($context) && $this->second->interpret($context);
19    }
20}

OrExp.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\Interpreter;
 6
 7/**
 8 * This NoTerminalExpression
 9 */
10class OrExp extends AbstractExp
11{
12    public function __construct(private AbstractExp $first, private AbstractExp $second)
13    {
14    }
15
16    public function interpret(Context $context): bool
17    {
18        return $this->first->interpret($context) || $this->second->interpret($context);
19    }
20}

3.3.5. Test

Tests/InterpreterTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\Interpreter\Tests;
 6
 7use DesignPatterns\Behavioral\Interpreter\AndExp;
 8use DesignPatterns\Behavioral\Interpreter\Context;
 9use DesignPatterns\Behavioral\Interpreter\OrExp;
10use DesignPatterns\Behavioral\Interpreter\VariableExp;
11use PHPUnit\Framework\TestCase;
12
13class InterpreterTest extends TestCase
14{
15    private Context $context;
16    private VariableExp $a;
17    private VariableExp $b;
18    private VariableExp $c;
19
20    public function setUp(): void
21    {
22        $this->context = new Context();
23        $this->a = new VariableExp('A');
24        $this->b = new VariableExp('B');
25        $this->c = new VariableExp('C');
26    }
27
28    public function testOr()
29    {
30        $this->context->assign($this->a, false);
31        $this->context->assign($this->b, false);
32        $this->context->assign($this->c, true);
33
34        // A ∨ B
35        $exp1 = new OrExp($this->a, $this->b);
36        $result1 = $exp1->interpret($this->context);
37
38        $this->assertFalse($result1, 'A ∨ B must false');
39
40        // $exp1 ∨ C
41        $exp2 = new OrExp($exp1, $this->c);
42        $result2 = $exp2->interpret($this->context);
43
44        $this->assertTrue($result2, '(A ∨ B) ∨ C must true');
45    }
46
47    public function testAnd()
48    {
49        $this->context->assign($this->a, true);
50        $this->context->assign($this->b, true);
51        $this->context->assign($this->c, false);
52
53        // A ∧ B
54        $exp1 = new AndExp($this->a, $this->b);
55        $result1 = $exp1->interpret($this->context);
56
57        $this->assertTrue($result1, 'A ∧ B must true');
58
59        // $exp1 ∧ C
60        $exp2 = new AndExp($exp1, $this->c);
61        $result2 = $exp2->interpret($this->context);
62
63        $this->assertFalse($result2, '(A ∧ B) ∧ C must false');
64    }
65}