diff --git a/src/RR/Shunt/Parser.php b/src/RR/Shunt/Parser.php index 0b438be..c294e51 100644 --- a/src/RR/Shunt/Parser.php +++ b/src/RR/Shunt/Parser.php @@ -68,16 +68,16 @@ public function __construct(Scanner $scanner) // maintain copy of queue $this->queueCopy = $this->queue; - } + } - private function reset() - { + private function reset() + { $this->queue = $this->queueCopy; $this->scanner->reset(); - } + } - public function reduce(Context $ctx) - { + public function reduce(Context $ctx) + { $this->reset(); $this->stack = array(); $len = 0; @@ -90,24 +90,30 @@ public function reduce(Context $ctx) case Token::T_NULL: case Token::T_IDENT: // determine constant value - if ($t->type === Token::T_IDENT) - $t = new Token(Token::T_NUMBER, $ctx->cs($t->value)); + if ($t->type === Token::T_IDENT) { + $value = $ctx->cs($t->value); + if ($value === 'null') { + $t = new Token(Token::T_NULL, null); + } else { + $t = new Token(Token::T_NUMBER, $value); + } + } // If the token is a value, null or identifier // Push it onto the stack. $this->stack[] = $t; ++$len; break; - - case Token::T_AND: - case Token::T_OR: - case Token::T_XOR: - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: + + case Token::T_AND: + case Token::T_OR: + case Token::T_XOR: + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: case Token::T_PLUS: case Token::T_MINUS: case Token::T_UNARY_PLUS: @@ -185,73 +191,73 @@ protected function op($op, $lhs, $rhs, Context $ctx) if ($lhs !== null) { $lhs = $lhs->value; $rhs = $rhs->value; - - switch ($op) - { - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - case Token::T_PLUS: - case Token::T_MINUS: - case Token::T_TIMES: - case Token::T_DIV: - case Token::T_MOD: - case Token::T_POW: - if (is_bool($lhs) && is_bool($rhs)) - { - throw new RuntimeError('run-time error: trying to do a number only operation over two booleans'); - } - else if (is_bool($lhs) || is_bool($rhs)) - { - throw new RuntimeError('run-time error: trying to calculate a value out of a decimal and a boolean'); - } - break; - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: - if (is_bool($lhs) ^ is_bool($rhs)) - { - throw new RuntimeError('run-time error: trying to calculate a value out of a decimal and a boolean'); - } - break; - case Token::T_AND: - case Token::T_OR: - case Token::T_XOR: - if (!is_bool($lhs) || !is_bool($rhs)) - { - throw new RuntimeError('run-time error: trying to do a boolean only operation over two numbers'); - } - break; - } - + + switch ($op) + { + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + case Token::T_PLUS: + case Token::T_MINUS: + case Token::T_TIMES: + case Token::T_DIV: + case Token::T_MOD: + case Token::T_POW: + if (is_bool($lhs) && is_bool($rhs)) + { + throw new RuntimeError('run-time error: trying to do a number only operation over two booleans'); + } + else if (is_bool($lhs) || is_bool($rhs)) + { + throw new RuntimeError('run-time error: trying to calculate a value out of a decimal and a boolean'); + } + break; + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: + if (is_bool($lhs) ^ is_bool($rhs)) + { + throw new RuntimeError('run-time error: trying to calculate a value out of a decimal and a boolean'); + } + break; + case Token::T_AND: + case Token::T_OR: + case Token::T_XOR: + if (!is_bool($lhs) || !is_bool($rhs)) + { + throw new RuntimeError('run-time error: trying to do a boolean only operation over two numbers'); + } + break; + } + switch ($op) { - case Token::T_AND: - return $lhs && $rhs; - - case Token::T_OR: - return $lhs || $rhs; - - case Token::T_XOR: - return $lhs ^ $rhs; - - case Token::T_GREATER_EQUAL: - return $lhs >= $rhs; - - case Token::T_LESS_EQUAL: - return $lhs <= $rhs; - - case Token::T_GREATER: - return $lhs > $rhs; - - case Token::T_LESS: - return $lhs < $rhs; - - case Token::T_EQUAL: - return $lhs == $rhs; - - case Token::T_NOT_EQUAL: - return $lhs != $rhs; - + case Token::T_AND: + return $lhs && $rhs; + + case Token::T_OR: + return $lhs || $rhs; + + case Token::T_XOR: + return $lhs ^ $rhs; + + case Token::T_GREATER_EQUAL: + return $lhs >= $rhs; + + case Token::T_LESS_EQUAL: + return $lhs <= $rhs; + + case Token::T_GREATER: + return $lhs > $rhs; + + case Token::T_LESS: + return $lhs < $rhs; + + case Token::T_EQUAL: + return $lhs == $rhs; + + case Token::T_NOT_EQUAL: + return $lhs != $rhs; + case Token::T_PLUS: return $lhs + $rhs; @@ -282,7 +288,7 @@ protected function op($op, $lhs, $rhs, Context $ctx) } switch ($op) { - + case Token::T_NOT: return is_null($rhs->value) ? null : (float)!$rhs->value; @@ -296,16 +302,16 @@ protected function op($op, $lhs, $rhs, Context $ctx) protected function argc(Token $t) { - switch ($t->type) { - case Token::T_AND: - case Token::T_OR: - case Token::T_XOR: - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: + switch ($t->type) { + case Token::T_AND: + case Token::T_OR: + case Token::T_XOR: + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: case Token::T_PLUS: case Token::T_MINUS: case Token::T_TIMES: @@ -410,15 +416,15 @@ protected function handle(Token $t) break; // If the token is an operator, op1, then: - case Token::T_AND: - case Token::T_OR: - case Token::T_XOR: - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: + case Token::T_AND: + case Token::T_OR: + case Token::T_XOR: + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: case Token::T_PLUS: case Token::T_MINUS: case Token::T_UNARY_PLUS: @@ -442,15 +448,15 @@ protected function handle(Token $t) default: break 2; - case Token::T_AND: - case Token::T_OR: - case Token::T_XOR: - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: + case Token::T_AND: + case Token::T_OR: + case Token::T_XOR: + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: case Token::T_PLUS: case Token::T_MINUS: case Token::T_UNARY_PLUS: @@ -516,18 +522,18 @@ protected function handle(Token $t) protected function assoc(Token $t) { - switch ($t->type) { - case Token::T_AND: - case Token::T_OR: - case Token::T_XOR: - - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: - + switch ($t->type) { + case Token::T_AND: + case Token::T_OR: + case Token::T_XOR: + + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: + case Token::T_TIMES: case Token::T_DIV: case Token::T_MOD: @@ -563,29 +569,29 @@ protected function preced(Token $t) case Token::T_DIV: case Token::T_MOD: return 7; - + case Token::T_PLUS: case Token::T_MINUS: return 6; - - case Token::T_GREATER_EQUAL: - case Token::T_LESS_EQUAL: - case Token::T_GREATER: - case Token::T_LESS: - return 5; - - case Token::T_EQUAL: - case Token::T_NOT_EQUAL: - return 4; - - case Token::T_XOR: - return 3; - - case Token::T_OR: - return 2; - - case Token::T_AND: - return 1; + + case Token::T_GREATER_EQUAL: + case Token::T_LESS_EQUAL: + case Token::T_GREATER: + case Token::T_LESS: + return 5; + + case Token::T_EQUAL: + case Token::T_NOT_EQUAL: + return 4; + + case Token::T_AND: + return 3; + + case Token::T_XOR: + return 2; + + case Token::T_OR: + return 1; } return 0; diff --git a/src/RR/Shunt/Scanner.php b/src/RR/Shunt/Scanner.php index 0c5b186..d797f8b 100644 --- a/src/RR/Shunt/Scanner.php +++ b/src/RR/Shunt/Scanner.php @@ -37,24 +37,24 @@ class Scanner { // operator_________________________________|number_______________|word____________________|space_ - const PATTERN = '/^([<>]=|<>|><|[!,><=&\|\+\-\*\/\^%\(\)]|\d*\.\d+|\d+\.\d*|\d+|[a-z_A-Zπ]+[a-z_A-Z0-9]*|[ \t]+)/'; + const PATTERN = '/^([<>]=|<>|><|[!,><=&\|\+\-\*\/\^%\(\)]|\d*\.\d+|\d+\.\d*|\d+|[a-z_A-Zπ]+[a-z_A-Z0-9]*|[ \t]+)/'; const ERR_EMPTY = 'nothing found! (endless loop) near: `%s`'; const ERR_MATCH = 'syntax error near `%s`'; protected $tokens = array( 0 ); - protected $lookup = array( - '>=' => Token::T_GREATER_EQUAL, - '<=' => Token::T_LESS_EQUAL, - '<>' => Token::T_NOT_EQUAL, - '><' => Token::T_XOR, - '>' => Token::T_GREATER, - '<' => Token::T_LESS, - '=' => Token::T_EQUAL, - '&' => Token::T_AND, - '|' => Token::T_OR, - + protected $lookup = array( + '>=' => Token::T_GREATER_EQUAL, + '<=' => Token::T_LESS_EQUAL, + '<>' => Token::T_NOT_EQUAL, + '><' => Token::T_XOR, + '>' => Token::T_GREATER, + '<' => Token::T_LESS, + '=' => Token::T_EQUAL, + '&' => Token::T_AND, + '|' => Token::T_OR, + '+' => Token::T_PLUS, '-' => Token::T_MINUS, '/' => Token::T_DIV, @@ -72,7 +72,7 @@ public function __construct($input) $prev = new Token(Token::T_OPERATOR, 'noop'); while (trim($input) !== '') { - + if (!preg_match(self::PATTERN, $input, $match)) { // syntax error throw new SyntaxError(sprintf(self::ERR_MATCH, substr($input, 0, 10))); @@ -85,7 +85,7 @@ public function __construct($input) // Remove the first matched token from the input, for the next iteration $input = substr($input, strlen($match[1])); - + // Get the value of the matched token $value = trim($match[1]); @@ -146,8 +146,8 @@ public function __construct($input) public function reset() { reset($this->tokens); } // call before reusing Scanner instance public function curr() { return current($this->tokens); } public function next() { return next($this->tokens); } - public function prev() { return prev($this->tokens); } - public function dump() { print_r($this->tokens); } + public function prev() { return prev($this->tokens); } + public function dump() { print_r($this->tokens); } public function peek() { diff --git a/src/RR/Shunt/Token.php b/src/RR/Shunt/Token.php index 5e2e9cb..6755952 100644 --- a/src/RR/Shunt/Token.php +++ b/src/RR/Shunt/Token.php @@ -35,32 +35,32 @@ class Token { - const T_NUMBER = 1, // a number (integer / double) - T_IDENT = 2, // constant - T_FUNCTION = 4, // function - T_POPEN = 8, // ( - T_PCLOSE = 16, // ) - T_COMMA = 32, // , - T_OPERATOR = 64, // operator (currently unused) - T_PLUS = 65, // + - T_MINUS = 66, // - - T_TIMES = 67, // * - T_DIV = 68, // / - T_MOD = 69, // % - T_POW = 70, // ^ - T_UNARY_PLUS = 71, // + unsigned number (determined during parsing) - T_UNARY_MINUS = 72, // - signed number (determined during parsing) - T_NOT = 73, // ! - T_NULL = 128, // null - T_GREATER_EQUAL = 256, // >= - T_LESS_EQUAL = 512, // <= - T_GREATER = 1024, // > - T_LESS = 2048, // < - T_EQUAL = 4096, // = - T_NOT_EQUAL = 8192, // <> - T_AND = 16384, // & - T_OR = 32768, // | - T_XOR = 65536; // >< + const T_NUMBER = 1, // a number (integer / double) + T_IDENT = 2, // constant + T_FUNCTION = 4, // function + T_POPEN = 8, // ( + T_PCLOSE = 16, // ) + T_COMMA = 32, // , + T_OPERATOR = 64, // operator (currently unused) + T_PLUS = 65, // + + T_MINUS = 66, // - + T_TIMES = 67, // * + T_DIV = 68, // / + T_MOD = 69, // % + T_POW = 70, // ^ + T_UNARY_PLUS = 71, // + unsigned number (determined during parsing) + T_UNARY_MINUS = 72, // - signed number (determined during parsing) + T_NOT = 73, // ! + T_NULL = 128, // null + T_GREATER_EQUAL = 256, // >= + T_LESS_EQUAL = 512, // <= + T_GREATER = 1024, // > + T_LESS = 2048, // < + T_EQUAL = 4096, // = + T_NOT_EQUAL = 8192, // <> + T_AND = 16384, // & + T_OR = 32768, // | + T_XOR = 65536; // >< public $type, $value, $argc = 0;