From aa8ffe19f2eb6f194b3e6ee1aac4c2706659e6b9 Mon Sep 17 00:00:00 2001 From: Bruce Wells Date: Thu, 4 Jun 2020 11:43:16 -0400 Subject: Variable fixes (#67) * Reproduce if throws UnknownOperatorException * Fix variable detection * Adding IncorrectNumberOfFunctionParametersException * Removing tabs * Better exception message text --- src/NXP/Classes/Calculator.php | 2 +- src/NXP/Classes/CustomFunction.php | 5 +- src/NXP/Classes/Operator.php | 1 - src/NXP/Classes/Tokenizer.php | 16 ++--- ...ncorrectNumberOfFunctionParametersException.php | 16 +++++ tests/MathTest.php | 68 ++++++++++++++++++++++ 6 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 src/NXP/Exception/IncorrectNumberOfFunctionParametersException.php diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php index 94c6933..bcb4b4e 100644 --- a/src/NXP/Classes/Calculator.php +++ b/src/NXP/Classes/Calculator.php @@ -77,7 +77,7 @@ class Calculator } $result = array_pop($stack); if ($result === null || !empty($stack)) { - throw new IncorrectExpressionException(); + throw new IncorrectExpressionException('Stack must be empty'); } return $result->value; } diff --git a/src/NXP/Classes/CustomFunction.php b/src/NXP/Classes/CustomFunction.php index 06e21ef..f127ad2 100644 --- a/src/NXP/Classes/CustomFunction.php +++ b/src/NXP/Classes/CustomFunction.php @@ -3,7 +3,7 @@ namespace NXP\Classes; -use NXP\Exception\IncorrectExpressionException; +use NXP\Exception\IncorrectNumberOfFunctionParametersException; use ReflectionException; use ReflectionFunction; @@ -30,6 +30,7 @@ class CustomFunction * @param callable $function * @param int $places * @throws ReflectionException + * @throws IncorrectNumberOfFunctionParametersException */ public function __construct(string $name, callable $function, ?int $places = null) { @@ -46,7 +47,7 @@ class CustomFunction public function execute(array &$stack) : Token { if (count($stack) < $this->places) { - throw new IncorrectExpressionException(); + throw new IncorrectNumberOfFunctionParametersException($this->name); } $args = []; for ($i = 0; $i < $this->places; $i++) { diff --git a/src/NXP/Classes/Operator.php b/src/NXP/Classes/Operator.php index 86df549..00485bc 100644 --- a/src/NXP/Classes/Operator.php +++ b/src/NXP/Classes/Operator.php @@ -46,7 +46,6 @@ class Operator $this->isRightAssoc = $isRightAssoc; $this->priority = $priority; $this->function = $function; - $this->function = $function; $reflection = new ReflectionFunction($function); $this->places = $reflection->getNumberOfParameters(); } diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php index 6b14677..203724a 100644 --- a/src/NXP/Classes/Tokenizer.php +++ b/src/NXP/Classes/Tokenizer.php @@ -157,14 +157,16 @@ class Tokenizer } $this->emptyNumberBufferAsLiteral(); $this->emptyStrBufferAsVariable(); - if (count($this->tokens) > 0) { - if ($this->tokens[count($this->tokens) - 1]->type === Token::Operator) { - $this->tokens[count($this->tokens) - 1]->value .= $ch; + if ($ch != '$') { + if (count($this->tokens) > 0) { + if ($this->tokens[count($this->tokens) - 1]->type === Token::Operator) { + $this->tokens[count($this->tokens) - 1]->value .= $ch; + } else { + $this->tokens[] = new Token(Token::Operator, $ch); + } } else { $this->tokens[] = new Token(Token::Operator, $ch); } - } else { - $this->tokens[] = new Token(Token::Operator, $ch); } $this->allowNegative = true; } @@ -251,12 +253,12 @@ class Tokenizer break; case Token::Operator: if (!array_key_exists($token->value, $this->operators)) { - throw new UnknownOperatorException(); + throw new UnknownOperatorException($token->value); } $op1 = $this->operators[$token->value]; while ($stack->count() > 0 && $stack->top()->type === Token::Operator) { if (!array_key_exists($stack->top()->value, $this->operators)) { - throw new UnknownOperatorException(); + throw new UnknownOperatorException($stack->top()->value); } $op2 = $this->operators[$stack->top()->value]; if ($op2->priority >= $op1->priority) { diff --git a/src/NXP/Exception/IncorrectNumberOfFunctionParametersException.php b/src/NXP/Exception/IncorrectNumberOfFunctionParametersException.php new file mode 100644 index 0000000..4c66bdf --- /dev/null +++ b/src/NXP/Exception/IncorrectNumberOfFunctionParametersException.php @@ -0,0 +1,16 @@ +expectException(DivisionByZeroException::class); $calculator->execute('10 / 0'); + $calculator->setVar('one', 1)->setVar('zero', 0); + $this->assertEquals(0.0, $calculator->execute('$one / $zero')); + } + + public function testVariableIncorrectExpressionException() + { + $calculator = new MathExecutor(); + $calculator->setVar('four', 4); + $this->assertEquals(4, $calculator->execute('$four')); + $this->expectException(IncorrectExpressionException::class); + $this->assertEquals(0.0, $calculator->execute('$')); + $this->assertEquals(0.0, $calculator->execute('$ + $four')); } public function testExponentiation() @@ -289,6 +303,60 @@ class MathTest extends TestCase $this->assertEquals(cos(2), $calculator->execute( 'if(cos(2), cos(2), 0)' )); + $trx_amount = 100000; + $calculator->setVar('trx_amount', $trx_amount); + $this->assertEquals($trx_amount, $calculator->execute('$trx_amount')); + $this->assertEquals($trx_amount * 0.03, $calculator->execute( + 'if($trx_amount < 40000, $trx_amount * 0.06, $trx_amount * 0.03)' + )); + $this->assertEquals($trx_amount * 0.03, $calculator->execute( + 'if($trx_amount < 40000, $trx_amount * 0.06, if($trx_amount < 60000, $trx_amount * 0.05, $trx_amount * 0.03))' + )); + $trx_amount = 39000; + $calculator->setVar('trx_amount', $trx_amount); + $this->assertEquals($trx_amount * 0.06, $calculator->execute( + 'if($trx_amount < 40000, $trx_amount * 0.06, if($trx_amount < 60000, $trx_amount * 0.05, $trx_amount * 0.03))' + )); + $trx_amount = 59000; + $calculator->setVar('trx_amount', $trx_amount); + $this->assertEquals($trx_amount * 0.05, $calculator->execute( + 'if($trx_amount < 40000, $trx_amount * 0.06, if($trx_amount < 60000, $trx_amount * 0.05, $trx_amount * 0.03))' + )); + $this->expectException(IncorrectNumberOfFunctionParametersException::class); + $this->assertEquals(0.0, $calculator->execute( + 'if($trx_amount < 40000, $trx_amount * 0.06)' + )); + } + + public function testVariables() + { + $calculator = new MathExecutor(); + $this->assertEquals(3.14159265359, $calculator->execute('$pi')); + $this->assertEquals(2.71828182846, $calculator->execute('$e')); + $calculator->setVars([ + 'trx_amount' => 100000, + 'ten' => 10, + 'nine' => 9, + 'eight' => 8, + 'seven' => 7, + 'six' => 6, + 'five' => 5, + 'four' => 4, + 'three' => 3, + 'two' => 2, + 'one' => 1, + 'zero' => 0, + ]); + $this->assertEquals(100000, $calculator->execute('$trx_amount')); + $this->assertEquals(10 - 9, $calculator->execute('$ten - $nine')); + $this->assertEquals(9 - 10, $calculator->execute('$nine - $ten')); + $this->assertEquals(10 + 9, $calculator->execute('$ten + $nine')); + $this->assertEquals(10 * 9, $calculator->execute('$ten * $nine')); + $this->assertEquals(10 / 9, $calculator->execute('$ten / $nine')); + $this->assertEquals(10 / (9 / 5), $calculator->execute('$ten / ($nine / $five)')); + + $this->expectException(UnknownVariableException::class); + $this->assertEquals(0.0, $calculator->execute('$unsetVariable')); } public function testEvaluateFunctionParameters() -- cgit v1.2.3