Making Languages
Part 5: Interpretation
Edit:
Anthony Ferrara points out that my wording was wrong here. Interpreters aren’t a kind of compiler — they’re a kind of machine. Compilers convert code from one language to another while interpreters do things with code in a specific language.
Let’s step back and think about what a program does. It sometimes accepts input, does some processing and sometimes returns some output. A program that doesn’t accept input or provide output is boring. The question is — how is that input provided and returned?
The answer (at least for our case) is a context. A context is an array we give a program. This array is changed by the processing and we can inspect it after the program has run to see what happened.
Interpreting
This means we should add a construct for writing to the context:
class Assignment
{
/**
* @var string
*/
protected $identity;
/**
* @var Addition|Number
*/
protected $value;
/**
* @param string $identity
* @param Addition|Number $value
*/
public function __construct($identity, $value)
{
$this->identity = $identity;
$this->value = $value;
}
/**
* @param array $context
*
* @return array
*/
public function apply(array &$context)
{
$value = $this->value->getValue();
$context[$this->identity] = $value;
return $value;
}
}
This is from Assignment.php
This class takes an $identity (variable name) parameter and a $value parameter. The apply() method uses these to store some contextual data. We also need to add a couple methods to the existing constructs:
class Number
{
/**
* @var int
*/
protected $value;
/**
* @param int $value
*
* @return Number
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* @return int
*/
public function getValue()
{
return $this->value;
}
}
This is from Number.php
class Addition
{
/**
* @var Addition|Number
*/
protected $left;
/**
* @var Addition|Number
*/
protected $right;
/**
* @param Addition|Number $left
* @param Addition|Number $right
*
* @return Addition
*/
public function __construct($left, $right)
{
$this->left = $left;
$this->right = $right;
}
/**
* @return int
*/
public function getValue()
{
return $this->left->getValue() + $this->right->getValue();
}
}
This is from Addition.php
These new getValue() methods return the underlying integer values. Addition constructs can be nested but the values of the Number instances are always returned. This is thanks to recursion!
We can use this new Assignment construct to write to a context:
$context = [];
$assignment1 = new Assignment('foo', new Number(1));
$assignment1->apply($context); // 1
$context; // ['foo' => 1]
$addition = new Addition(
new Addition(
new Number(1),
new Number(2)
),
new Number(3)
);
$assignment2 = new Assignment('bar', $addition);
$assignment2->apply($context); // 1
$context; // ['foo' => 1, 'bar' => 6]