The script in (almost) full
As opposed to wasting space by printing such a long script several times over, I have only been printing parts so far. However, as we are done, here is the complete script, sans the gettoken() function which is unchanged:
<?php
define("FOO_NUMBER", 0);
define("FOO_VARIABLE", 1);
define("FOO_ASSIGNEQUALS", 2);
define("FOO_PRINT", 3);
define("FOO_STRING", 4);
define("FOO_SEMICOLON", 5);
define("FOO_PLUS", 6);
define("FOO_MULTIPLY", 7);
define("IS_OPERATOR", 0);
define("IS_OPERAND", 1);
// you can change the values in $precedence, as long you keep the order the same
$precedence = array (FOO_PRINT => 0, FOO_ASSIGNEQUALS => 1, FOO_PLUS => 2, FOO_MULTIPLY => 3);
class token {
public $type;
public $token;
public $val;
public function __construct($type, $token, $val) {
$this->type = $type;
$this->token = $token;
$this->val = $val;
}
}
// this function converts variables to values as appropriate
function getval($token) {
GLOBAL $variables;
if ($token->token == FOO_VARIABLE) {
return $variables[$token->val];
} else {
return $token->val;
}
}
function execute(&$stack) {
GLOBAL $variables;
$operator = array_pop($stack);
if ($stack[count($stack) - 1]->type == IS_OPERATOR) {
$right = execute($stack);
} else {
$right = array_pop($stack);
}
if (count($stack)) {
if ($stack[count($stack) - 1]->type == IS_OPERATOR) {
$left = execute($stack);
} else {
$left = array_pop($stack);
}
}
switch($operator->token) {
case FOO_ASSIGNEQUALS:
$variables[$left->val] = getval($right);
break;
case FOO_PLUS:
return new token(IS_OPERAND, FOO_NUMBER, getval($left) + getval($right));
case FOO_MULTIPLY:
return new token(IS_OPERAND, FOO_NUMBER, getval($left) * getval($right));
case FOO_PRINT:
print getval($right);
print "\n";
}
}
function main() {
GLOBAL $lasttoken, $precedence;
while(1) {
$stack = array();
$operators = array();
do {
$token = gettoken();
switch($token) {
case FOO_NUMBER:
case FOO_VARIABLE:
case FOO_STRING:
$stack[] = new token(IS_OPERAND, $token, $lasttoken);
break;
default:
if ($token != FOO_SEMICOLON) {
// this removes higher-precedence operators in places of a new, lower-precedence one
while (count($operators) && $precedence[$operators[count($operators) - 1]->token] > $precedence[$token]) {
$higher_op = array_pop($operators);
array_push($stack, $higher_op);
}
$operators[] = new token(IS_OPERATOR, $token, NULL);
}
}
} while ($token != FOO_SEMICOLON);
while (count($operators)) array_push($stack, array_pop($operators));
execute($stack);
}
}
function gettoken() {
// this is unchanged
}
$script = fopen("script.foo", "r");
$characters = array_merge(range('a', 'z'), range('A', 'z'));
$characters[] = "_";
$variables = array();
function cleanup() {
GLOBAL $script;
fclose($script);
}
register_shutdown_function("cleanup");
main();
?>
Even with the gettoken() function all but removed, that is still probably the longest script in the entire book. Still, you like typing, right?
Next chapter: Mini-language conclusion >>
Previous chapter: Operator precedence
Jump to:
Home: Table of Contents



Copyright 2012 Future Publishing Limited (company
registered number 2008885), a company registered
in England and Wales whose registered office is at
Beauford Court, 30 Monmouth Street, Bath, BA1 2BW, UK