3.3. Interpreter

3.3.1. Scopo

Per un dato linguaggio, definisce la rappresentazione della sua grammatica come «Espressioni non terminali» ed «Espressioni Terminali» «così come un interprete per le frasi di quel linguaggio.

3.3.2. Esempi

  • Un esempio di interprete binario, dove ogni termine è definito dalla propria classe.

3.3.3. Diagramma UML

Alt Interpreter UML Diagram

3.3.4. Codice

Potete trovare questo codice anche su 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}