public function testParserWithStringConstants()
 {
     $context = new Context();
     $const = 'string constant';
     $context->def('const', $const, 'string');
     $actual = Parser::parse('const', $context);
     $this->assertEquals($const, $actual);
 }
 /**
  * @param $equation
  * @param array $constants
  *                         @param $expected
  *
  * @dataProvider equationAndConstantsProvider
  */
 public function testParserWithConstants($equation, array $constants, $expected)
 {
     $context = new Context();
     foreach ($constants as $key => $val) {
         $context->def($key, $val);
     }
     $actual = Parser::parse($equation, $context);
     $this->assertEquals($expected, $actual);
 }
 public function testFunctionDefinitionWithOptionalParams()
 {
     $context = new Context();
     $context->def('func', function ($param1, $param2 = 100) {
         return $param1 + $param2;
     });
     $actual = $context->fn('func', array(3));
     $this->assertEquals(103.0, $actual);
     $actual = $context->fn('func', array(3, 200));
     $this->assertEquals(203.0, $actual);
 }
 public function testWrapPHPFunction()
 {
     $context = new Context();
     $context->def('abs');
     $equation = 'abs(100)';
     $actual = Parser::parse($equation, $context);
     $expected = 100;
     $this->assertEquals($expected, $actual);
     $equation = 'abs(-100)';
     $actual = Parser::parse($equation, $context);
     $expected = 100;
     $this->assertEquals($expected, $actual);
 }
Пример #5
0
<?php

// Composer autoloading
if (file_exists('vendor/autoload.php')) {
    $loader = (include 'vendor/autoload.php');
}
use RR\Shunt\Parser;
use RR\Shunt\Context;
// einfach
$trm = '3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3';
print Parser::parse($trm);
// 3.0001220703125
print "\n";
// mit eigenen konstanten und funktionen
$ctx = new Context();
$ctx->def('abs');
// wrapper
$ctx->def('foo', 5);
$ctx->def('bar', function ($a, $b) {
    return $a * $b;
});
$trm = '3 + bar(4, 2) / (abs(-1) - foo) ^ 2 ^ 3';
print Parser::parse($trm, $ctx);
// 3.0001220703125
 /**
  * @expectedException \RR\Shunt\Exception\RuntimeError
  */
 public function testCallNotsetConstantCausesException()
 {
     $context = new Context();
     $context->cs('notdefinedfunction');
 }
 /**
  * @expectedException RR\Shunt\Exception\ParseError
  */
 public function testParserExceptionSurplusClosingBracket()
 {
     $context = new Context();
     $context->def('pi');
     $equation = 'pi())';
     $actual = Parser::parse($equation, $context);
 }
Пример #8
0
 public function reduce(Context $ctx)
 {
     $this->stack = array();
     $len = 0;
     // While there are input tokens left
     // Read the next token from input.
     while ($t = array_shift($this->queue)) {
         switch ($t->type) {
             case Token::T_NUMBER:
             case Token::T_IDENT:
                 // wert einer konstanten ermitteln
                 if ($t->type === Token::T_IDENT) {
                     $t = new Token(Token::T_NUMBER, $ctx->cs($t->value));
                 }
                 // If the token is a value or identifier
                 // Push it onto the stack.
                 $this->stack[] = $t;
                 ++$len;
                 break;
             case Token::T_PLUS:
             case Token::T_MINUS:
             case Token::T_UNARY_PLUS:
             case Token::T_UNARY_MINUS:
             case Token::T_TIMES:
             case Token::T_DIV:
             case Token::T_MOD:
             case Token::T_POW:
             case Token::T_NOT:
                 // It is known a priori that the operator takes n arguments.
                 $na = $this->argc($t);
                 // If there are fewer than n values on the stack
                 if ($len < $na) {
                     throw new RuntimeError('laufzeit fehler: zu wenig paramter für operator "' . $t->value . '" (' . $na . ' -> ' . $len . ')');
                 }
                 $rhs = array_pop($this->stack);
                 $lhs = null;
                 if ($na > 1) {
                     $lhs = array_pop($this->stack);
                 }
                 // if ($lhs) print "{$lhs->value} {$t->value} {$rhs->value}\n";
                 // else print "{$t->value} {$rhs->value}\n";
                 $len -= $na - 1;
                 // Push the returned results, if any, back onto the stack.
                 $this->stack[] = new Token(Token::T_NUMBER, $this->op($t->type, $lhs, $rhs));
                 break;
             case Token::T_FUNCTION:
                 // function
                 $argc = $t->argc;
                 $argv = array();
                 $len -= $argc - 1;
                 for (; $argc > 0; --$argc) {
                     array_unshift($argv, array_pop($this->stack)->value);
                 }
                 // Push the returned results, if any, back onto the stack.
                 $this->stack[] = new Token(Token::T_NUMBER, $ctx->fn($t->value, $argv));
                 break;
             default:
                 throw new RuntimeError('laufzeit fehler: unerwarteter token `' . $t->value . '`');
         }
     }
     // If there is only one value in the stack
     // That value is the result of the calculation.
     if (count($this->stack) === 1) {
         return array_pop($this->stack)->value;
     }
     // If there are more values in the stack
     // (Error) The user input has too many values.
     throw new RuntimeError('laufzeit fehler: zu viele werte im stack');
 }
Пример #9
0
 protected function op($op, $lhs, $rhs, Context $ctx)
 {
     // If there is a custom operator handler function defined in the context, call it instead
     if ($ctx->hasCustomOperatorHandler($op)) {
         $lhsValue = is_object($lhs) ? $lhs->value : null;
         $rhsValue = is_object($rhs) ? $rhs->value : null;
         return $ctx->execCustomOperatorHandler($op, $lhsValue, $rhsValue);
     }
     if ($lhs !== null) {
         $lhs = $lhs->value;
         $rhs = $rhs->value;
         switch ($op) {
             case Token::T_PLUS:
                 return $lhs + $rhs;
             case Token::T_MINUS:
                 return $lhs - $rhs;
             case Token::T_TIMES:
                 return $lhs * $rhs;
             case Token::T_DIV:
                 if ($rhs === 0.0) {
                     throw new RuntimeError('run-time error: division by zero');
                 }
                 return $lhs / $rhs;
             case Token::T_MOD:
                 if ($rhs === 0.0) {
                     throw new RuntimeError('run-time error: rest-division by zero');
                 }
                 return (double) $lhs % $rhs;
             case Token::T_POW:
                 return (double) pow($lhs, $rhs);
         }
         // throw?
         return 0;
     }
     switch ($op) {
         case Token::T_NOT:
             return is_null($rhs->value) ? null : (double) (!$rhs->value);
         case Token::T_UNARY_MINUS:
             return is_null($rhs->value) ? null : -$rhs->value;
         case Token::T_UNARY_PLUS:
             return is_null($rhs->value) ? null : +$rhs->value;
     }
 }
Пример #10
0
// Composer autoloading
if (file_exists('vendor/autoload.php')) {
    $loader = (include 'vendor/autoload.php');
}
use RR\Shunt\Parser;
use RR\Shunt\Context;
// einfach
$trm = '3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3';
print Parser::parse($trm) . "\n";
// 3.0001220703125
// mit eigenen konstanten und funktionen
$ctx = new Context();
$ctx->def('abs');
// wrapper
$ctx->def('foo', 5);
$ctx->def('bar', function ($a, $b) {
    return $a * $b;
});
$trm = '3 + bar(4, 2) / (abs(-1) - foo) ^ 2 ^ 3';
print Parser::parse($trm, $ctx) . "\n";
// 3.0001220703125
// mit string konstanten
$ctx = new Context();
$ctx->def('groupA', 'A', 'string');
// string constant
$ctx->def('isgroupA', function ($g) {
    return $g == 'A' ? 1 : 0;
});
$trm = 'isgroupA(groupA)';
print Parser::parse($trm, $ctx) . "\n";
// 1
Пример #11
0
 protected function op($op, $lhs, $rhs, Context $ctx)
 {
     // If there is a custom operator handler function defined in the context, call it instead
     if ($ctx->hasCustomOperatorHandler($op)) {
         $lhsValue = is_object($lhs) ? $lhs->value : null;
         $rhsValue = is_object($rhs) ? $rhs->value : null;
         return $ctx->execCustomOperatorHandler($op, $lhsValue, $rhsValue);
     }
     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_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;
             case Token::T_MINUS:
                 return $lhs - $rhs;
             case Token::T_TIMES:
                 return $lhs * $rhs;
             case Token::T_DIV:
                 if ($rhs === 0.0) {
                     throw new RuntimeError('run-time error: division by zero');
                 }
                 return $lhs / $rhs;
             case Token::T_MOD:
                 if ($rhs === 0.0) {
                     throw new RuntimeError('run-time error: rest-division by zero');
                 }
                 return (double) $lhs % $rhs;
             case Token::T_POW:
                 return (double) pow($lhs, $rhs);
         }
         // throw?
         return 0;
     }
     switch ($op) {
         case Token::T_NOT:
             return is_null($rhs->value) ? null : (double) (!$rhs->value);
         case Token::T_UNARY_MINUS:
             return is_null($rhs->value) ? null : -$rhs->value;
         case Token::T_UNARY_PLUS:
             return is_null($rhs->value) ? null : +$rhs->value;
     }
 }