Skip to content

Instantly share code, notes, and snippets.

@jelmervdl
Created June 12, 2012 08:47
Show Gist options
  • Save jelmervdl/2916265 to your computer and use it in GitHub Desktop.
Save jelmervdl/2916265 to your computer and use it in GitHub Desktop.
Interpreter in PHP
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
class Scope
{
public $variables;
public $functions;
public function __construct()
{
$this->variables = array();
$this->functions = array();
}
public function extend(array $variables)
{
$new_scope = clone $this;
$new_scope->variables = array_merge($this->variables, $variables);
return $new_scope;
}
}
abstract class Expression
{
abstract public function evaluate(Scope $scope);
}
abstract class BinaryExpression extends Expression
{
protected $left;
protected $right;
public function __construct(Expression $left, Expression $right)
{
$this->left = $left;
$this->right = $right;
}
}
class AdditionExpression extends BinaryExpression
{
public function evaluate(Scope $scope)
{
return $this->left->evaluate($scope) + $this->right->evaluate($scope);
}
}
class MultiplyExpression extends BinaryExpression
{
public function evaluate(Scope $scope)
{
return $this->left->evaluate($scope) * $this->right->evaluate($scope);
}
}
class NumberExpression extends Expression
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function evaluate(Scope $scope)
{
return $this->value;
}
}
class VariableExpression extends Expression
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function evaluate(Scope $scope)
{
return $scope->variables[$this->name];
}
}
class FunctionDefinition
{
private $body;
public function __construct(array $arguments, Expression $body)
{
$this->body = $body;
$this->arguments = $arguments;
}
public function call(Scope $scope, array $arguments)
{
$variables = array_combine($this->arguments, $arguments);
$function_scope = $scope->extend($variables);
return $this->body->evaluate($function_scope);
}
}
class FunctionCallExpression extends Expression
{
private $name;
private $arguments;
public function __construct($name, array $arguments)
{
$this->name = $name;
$this->arguments = $arguments;
}
public function evaluate(Scope $scope)
{
$arguments = array();
foreach ($this->arguments as $argument)
$arguments[] = $argument->evaluate($scope);
return $scope->functions[$this->name]->call($scope, $arguments);
}
}
class TestExpression extends Expression
{
private $test;
private $case_true;
private $case_false;
public function __construct(Expression $test, Expression $case_true, Expression $case_false)
{
$this->test = $test;
$this->case_true = $case_true;
$this->case_false = $case_false;
}
public function evaluate(Scope $scope)
{
$answer = $this->test->evaluate($scope);
return $answer
? $this->case_true->evaluate($scope)
: $this->case_false->evaluate($scope);
}
}
class EqualsExpression extends BinaryExpression
{
public function evaluate(Scope $scope)
{
return $this->left->evaluate($scope) == $this->right->evaluate($scope);
}
}
class GreaterThanExpression extends BinaryExpression
{
public function evaluate(Scope $scope)
{
return $this->left->evaluate($scope) > $this->right->evaluate($scope);
}
}
// if n == 0 then 1 else n * fac (n - 1)
// sum (n) = n > 0 ? n + sum (n - 1) : 0
function test()
{
$scope = new Scope();
$scope->variables['a'] = 24;
$sum = new FunctionDefinition(
array('n'),
new TestExpression(
new GreaterThanExpression(
new VariableExpression('n'),
new NumberExpression(1)
),
new AdditionExpression(
new VariableExpression('n'),
new FunctionCallExpression('sum',
array(new AdditionExpression(
new VariableExpression('n'),
new NumberExpression(-1)
))
)
),
new NumberExpression(0)
)
);
$fac = new FunctionDefinition(
array('n'),
new TestExpression(
// test
new EqualsExpression(
new VariableExpression('n'),
new NumberExpression(0)
),
// if true
new NumberExpression(1),
// else
new MultiplyExpression(
new VariableExpression('n'),
new FunctionCallExpression('fac',
array(new AdditionExpression(
new VariableExpression('n'),
new NumberExpression(-1)
)))
)
));
$add = new FunctionDefinition(
array('b'),
new AdditionExpression(
new VariableExpression('a'),
new VariableExpression('b')
));
$scope->functions['fac'] = $fac;
$scope->functions['sum'] = $sum;
$scope->functions['add'] = $add;
// $expr = new FunctionCallExpression('sum', array(new NumberExpression(3)));
// $expr = new FunctionCallExpression('fac', array(new NumberExpression(10)));
$expr = new FunctionCallExpression('add', array(new NumberExpression(2)));
echo $expr->evaluate($scope) . "\n";
var_dump($scope->variables);
}
test();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment