1.3. Interpreter¶
1.3.1. Intent¶
According to the Gang of Four, the Interpreter pattern is defined like that: “Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language” (Design Patterns: Elements of Reusable Object-Oriented Software, 2013, p. 243).
1.3.2. When to use it?¶
The Interpreter pattern should be used when a language has to be interpreted. It is better if the grammar used is pretty simple and if the efficiency is not a major concern.
1.3.3. Diagram¶
Created using PhpStorm and yFiles.
1.3.4. Implementation¶
ExpressionInterface.php
1 2 3 4 5 6 7 8 9 10 11 12 | <?php
namespace Phpatterns\Behavioral\Interpreter;
interface ExpressionInterface
{
/**
* @param array $context
* @return int
*/
public function interpret(array $context);
}
|
Multiply.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php
namespace Phpatterns\Behavioral\Interpreter\Expression;
use Phpatterns\Behavioral\Interpreter;
class Multiply implements Interpreter\ExpressionInterface
{
/** @var Interpreter\ExpressionInterface */
private $left;
/** @var Interpreter\ExpressionInterface */
private $right;
public function __construct(Interpreter\ExpressionInterface $left, Interpreter\ExpressionInterface $right)
{
$this->left = $left;
$this->right = $right;
}
public function interpret(array $context)
{
return $this->left->interpret($context) * $this->right->interpret($context);
}
}
|
Plus.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php
namespace Phpatterns\Behavioral\Interpreter\Expression;
use Phpatterns\Behavioral\Interpreter;
class Plus implements Interpreter\ExpressionInterface
{
/** @var Interpreter\ExpressionInterface */
private $left;
/** @var Interpreter\ExpressionInterface */
private $right;
public function __construct(Interpreter\ExpressionInterface $left, Interpreter\ExpressionInterface $right)
{
$this->left = $left;
$this->right = $right;
}
public function interpret(array $context)
{
return $this->left->interpret($context) + $this->right->interpret($context);
}
}
|
Number.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?php
namespace Phpatterns\Behavioral\Interpreter\Expression;
use Phpatterns\Behavioral\Interpreter;
class Number implements Interpreter\ExpressionInterface
{
/** @var int */
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function interpret(array $context)
{
return $this->value;
}
}
|
Variable.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php
namespace Phpatterns\Behavioral\Interpreter\Expression;
use Phpatterns\Behavioral\Interpreter;
class Variable implements Interpreter\ExpressionInterface
{
/** @var string */
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function interpret(array $context)
{
if (isset($context[$this->name])) {
return $context[$this->name]->interpret($context);
}
throw new \Exception('Missing context value!');
}
}
|
Parser.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <?php
namespace Phpatterns\Behavioral\Interpreter;
use Phpatterns\Behavioral\Interpreter\Expression;
class Parser
{
/** @var ExpressionInterface */
private $expressionTree;
/**
* @param string $strExpression
*/
public function __construct($strExpression)
{
$expressionStack = [];
foreach (explode(' ', $strExpression) as $part) {
if ('*' === $part) {
$right = array_pop($expressionStack);
$left = array_pop($expressionStack);
array_push($expressionStack, new Expression\Multiply($left, $right));
} elseif ('+' === $part) {
$right = array_pop($expressionStack);
$left = array_pop($expressionStack);
array_push($expressionStack, new Expression\Plus($left, $right));
} elseif (is_numeric($part)) {
array_push($expressionStack, new Expression\Number($part));
} else {
array_push($expressionStack, new Expression\Variable($part));
}
}
$this->expressionTree = array_pop($expressionStack);
}
public function interpret(array $context)
{
return $this->expressionTree->interpret($context);
}
}
|
1.3.5. Tests¶
InterpreterTest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <?php
namespace Test\Phpatterns\Behavioral\Interpreter;
use Phpatterns\Behavioral\Interpreter;
use Phpatterns\Behavioral\Interpreter\Expression;
class InterpreterTest extends \PHPUnit_Framework_TestCase
{
public function testInterpreterMechanismWithContext()
{
$firstContext = [
'x' => new Expression\Number(9),
'y' => new Expression\Number(3)
];
$this->assertSame(
30,
(new Interpreter\Parser('3 x * y +'))->interpret($firstContext)
);
$secondContext = [
'w' => new Expression\Number(10),
'x' => new Expression\Number(3),
'y' => new Expression\Number(7),
'z' => new Expression\Number(4)
];
$this->assertSame(
628,
(new Interpreter\Parser('w 5 x * * y + z *'))->interpret($secondContext)
);
}
public function testInterpreterMechanismWithEmptyContext()
{
$this->assertSame(
22,
(new Interpreter\Parser('3 5 * 7 +'))->interpret([])
);
$this->assertSame(
2600,
(new Interpreter\Parser('3 5 2 * * 100 + 20 *'))->interpret([])
);
}
public function testInterpreterMechanismWithMissingContext()
{
$this->setExpectedException('Exception', 'Missing context value!');
(new Interpreter\Parser('3 x * y +'))->interpret(['x' => new Expression\Number(9)]);
}
}
|