Support for better invalid expression detection and divide by zero (#30)
* Additional validation for bad expressions (*+ for example) * Removing DivisionByZeroException testing for now Added more unit tests.
This commit is contained in:
parent
4a672cfd94
commit
43f0ff3f28
8 changed files with 111 additions and 4 deletions
|
@ -54,7 +54,7 @@ class Calculator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$result = array_pop($stack);
|
$result = array_pop($stack);
|
||||||
if (!empty($stack)) {
|
if ($result === null || ! empty($stack)) {
|
||||||
throw new IncorrectExpressionException();
|
throw new IncorrectExpressionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace NXP\Classes\Token;
|
namespace NXP\Classes\Token;
|
||||||
|
|
||||||
|
use NXP\Exception\IncorrectExpressionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alexander Kiryukhin <alexander@symdev.org>
|
* @author Alexander Kiryukhin <alexander@symdev.org>
|
||||||
*/
|
*/
|
||||||
|
@ -41,12 +43,20 @@ class TokenDegree extends AbstractOperator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InterfaceToken[] $stack
|
* @param InterfaceToken[] $stack
|
||||||
|
*
|
||||||
* @return TokenNumber
|
* @return TokenNumber
|
||||||
|
*
|
||||||
|
* @throws \NXP\Exception\IncorrectExpressionException
|
||||||
*/
|
*/
|
||||||
public function execute(&$stack)
|
public function execute(&$stack)
|
||||||
{
|
{
|
||||||
$op2 = array_pop($stack);
|
$op2 = array_pop($stack);
|
||||||
$op1 = array_pop($stack);
|
$op1 = array_pop($stack);
|
||||||
|
|
||||||
|
if ($op1 === null || $op2 === null) {
|
||||||
|
throw new IncorrectExpressionException("Power operator requires two operators");
|
||||||
|
}
|
||||||
|
|
||||||
$result = $op1->getValue() ** $op2->getValue();
|
$result = $op1->getValue() ** $op2->getValue();
|
||||||
|
|
||||||
return new TokenNumber($result);
|
return new TokenNumber($result);
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
namespace NXP\Classes\Token;
|
namespace NXP\Classes\Token;
|
||||||
|
|
||||||
|
use NXP\Exception\IncorrectExpressionException;
|
||||||
|
use NXP\Exception\DivisionByZeroException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alexander Kiryukhin <alexander@symdev.org>
|
* @author Alexander Kiryukhin <alexander@symdev.org>
|
||||||
*/
|
*/
|
||||||
|
@ -41,12 +44,21 @@ class TokenDivision extends AbstractOperator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InterfaceToken[] $stack
|
* @param InterfaceToken[] $stack
|
||||||
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
*
|
||||||
|
* @throws \NXP\Exception\IncorrectExpressionException
|
||||||
|
* @throws \NXP\Exception\DivisionByZeroException
|
||||||
*/
|
*/
|
||||||
public function execute(&$stack)
|
public function execute(&$stack)
|
||||||
{
|
{
|
||||||
$op2 = array_pop($stack);
|
$op2 = array_pop($stack);
|
||||||
$op1 = array_pop($stack);
|
$op1 = array_pop($stack);
|
||||||
|
|
||||||
|
if ($op1 === null || $op2 === null) {
|
||||||
|
throw new IncorrectExpressionException("Division requires two operators");
|
||||||
|
}
|
||||||
|
|
||||||
$result = $op2->getValue() != 0 ? $op1->getValue() / $op2->getValue() : 0;
|
$result = $op2->getValue() != 0 ? $op1->getValue() / $op2->getValue() : 0;
|
||||||
|
|
||||||
return new TokenNumber($result);
|
return new TokenNumber($result);
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace NXP\Classes\Token;
|
namespace NXP\Classes\Token;
|
||||||
|
|
||||||
|
use NXP\Exception\IncorrectExpressionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alexander Kiryukhin <alexander@symdev.org>
|
* @author Alexander Kiryukhin <alexander@symdev.org>
|
||||||
*/
|
*/
|
||||||
|
@ -41,12 +43,20 @@ class TokenMinus extends AbstractOperator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InterfaceToken[] $stack
|
* @param InterfaceToken[] $stack
|
||||||
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
*
|
||||||
|
* @throws \NXP\Exception\IncorrectExpressionException
|
||||||
*/
|
*/
|
||||||
public function execute(&$stack)
|
public function execute(&$stack)
|
||||||
{
|
{
|
||||||
$op2 = array_pop($stack);
|
$op2 = array_pop($stack);
|
||||||
$op1 = array_pop($stack);
|
$op1 = array_pop($stack);
|
||||||
|
|
||||||
|
if ($op1 === null || $op2 === null) {
|
||||||
|
throw new IncorrectExpressionException("Subtraction requires two operators");
|
||||||
|
}
|
||||||
|
|
||||||
$result = $op1->getValue() - $op2->getValue();
|
$result = $op1->getValue() - $op2->getValue();
|
||||||
|
|
||||||
return new TokenNumber($result);
|
return new TokenNumber($result);
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace NXP\Classes\Token;
|
namespace NXP\Classes\Token;
|
||||||
|
|
||||||
|
use NXP\Exception\IncorrectExpressionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alexander Kiryukhin <alexander@symdev.org>
|
* @author Alexander Kiryukhin <alexander@symdev.org>
|
||||||
*/
|
*/
|
||||||
|
@ -41,12 +43,20 @@ class TokenMultiply extends AbstractOperator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InterfaceToken[] $stack
|
* @param InterfaceToken[] $stack
|
||||||
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
*
|
||||||
|
* @throws \NXP\Exception\IncorrectExpressionException
|
||||||
*/
|
*/
|
||||||
public function execute(&$stack)
|
public function execute(&$stack)
|
||||||
{
|
{
|
||||||
$op2 = array_pop($stack);
|
$op2 = array_pop($stack);
|
||||||
$op1 = array_pop($stack);
|
$op1 = array_pop($stack);
|
||||||
|
|
||||||
|
if ($op1 === null || $op2 === null) {
|
||||||
|
throw new IncorrectExpressionException("Multiplication requires two operators");
|
||||||
|
}
|
||||||
|
|
||||||
$result = $op1->getValue() * $op2->getValue();
|
$result = $op1->getValue() * $op2->getValue();
|
||||||
|
|
||||||
return new TokenNumber($result);
|
return new TokenNumber($result);
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace NXP\Classes\Token;
|
namespace NXP\Classes\Token;
|
||||||
|
|
||||||
|
use NXP\Exception\IncorrectExpressionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alexander Kiryukhin <alexander@symdev.org>
|
* @author Alexander Kiryukhin <alexander@symdev.org>
|
||||||
*/
|
*/
|
||||||
|
@ -41,12 +43,20 @@ class TokenPlus extends AbstractOperator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InterfaceToken[] $stack
|
* @param InterfaceToken[] $stack
|
||||||
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
*
|
||||||
|
* @throws \NXP\Exception\IncorrectExpressionException
|
||||||
*/
|
*/
|
||||||
public function execute(&$stack)
|
public function execute(&$stack)
|
||||||
{
|
{
|
||||||
$op2 = array_pop($stack);
|
$op2 = array_pop($stack);
|
||||||
$op1 = array_pop($stack);
|
$op1 = array_pop($stack);
|
||||||
|
|
||||||
|
if ($op1 === null || $op2 === null) {
|
||||||
|
throw new IncorrectExpressionException("Addition requires two operators");
|
||||||
|
}
|
||||||
|
|
||||||
$result = $op1->getValue() + $op2->getValue();
|
$result = $op1->getValue() + $op2->getValue();
|
||||||
|
|
||||||
return new TokenNumber($result);
|
return new TokenNumber($result);
|
||||||
|
|
19
src/NXP/Exception/DivisionByZeroException.php
Normal file
19
src/NXP/Exception/DivisionByZeroException.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of the MathExecutor package
|
||||||
|
*
|
||||||
|
* (c) Alexander Kiryukhin
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace NXP\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vitaliy Zhuk <zhuk2205@gmail.com>
|
||||||
|
*/
|
||||||
|
class DivisionByZeroException extends MathExecutorException
|
||||||
|
{
|
||||||
|
}
|
|
@ -11,7 +11,15 @@
|
||||||
|
|
||||||
namespace NXP\Tests;
|
namespace NXP\Tests;
|
||||||
|
|
||||||
use \NXP\MathExecutor;
|
use NXP\MathExecutor;
|
||||||
|
use NXP\Exception\DivisionByZeroException;
|
||||||
|
use NXP\Exception\IncorrectBracketsException;
|
||||||
|
use NXP\Exception\IncorrectExpressionException;
|
||||||
|
use NXP\Exception\MathExecutorException;
|
||||||
|
use NXP\Exception\UnknownFunctionException;
|
||||||
|
use NXP\Exception\UnknownOperatorException;
|
||||||
|
use NXP\Exception\UnknownTokenException;
|
||||||
|
use NXP\Exception\UnknownVariableException;
|
||||||
|
|
||||||
class MathTest extends \PHPUnit_Framework_TestCase
|
class MathTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
@ -27,10 +35,28 @@ class MathTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals($calculator->execute($expression), $phpResult);
|
$this->assertEquals($calculator->execute($expression), $phpResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUnknownFunctionException()
|
||||||
|
{
|
||||||
|
$calculator = new MathExecutor();
|
||||||
|
$this->expectException(UnknownFunctionException::class);
|
||||||
|
$calculator->execute('1 * fred("wilma") + 3');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIncorrectExpressionException()
|
||||||
|
{
|
||||||
|
$calculator = new MathExecutor();
|
||||||
|
$this->expectException(IncorrectExpressionException::class);
|
||||||
|
$calculator->execute('1 * + ');
|
||||||
|
}
|
||||||
|
|
||||||
public function testZeroDivision()
|
public function testZeroDivision()
|
||||||
{
|
{
|
||||||
$calculator = new MathExecutor();
|
$calculator = new MathExecutor();
|
||||||
$this->assertEquals($calculator->execute('1 / 0'), 0);
|
$this->assertEquals($calculator->execute('1 / 0'), 0);
|
||||||
|
|
||||||
|
// future version with allow for optional exceptions on divide by zero
|
||||||
|
// $this->expectException(DivisionByZeroException::class);
|
||||||
|
// $calculator->execute('1 / 0');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExponentiation()
|
public function testExponentiation()
|
||||||
|
@ -63,15 +89,25 @@ class MathTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
['(5 + 3) * -1'],
|
['(5 + 3) * -1'],
|
||||||
|
|
||||||
['2+2*2'],
|
['2- 2*2'],
|
||||||
|
['2-(2*2)'],
|
||||||
|
['(2- 2)*2'],
|
||||||
|
['2 + 2*2'],
|
||||||
|
['2+ 2*2'],
|
||||||
['(2+2)*2'],
|
['(2+2)*2'],
|
||||||
['(2+2)*-2'],
|
['(2 + 2)*-2'],
|
||||||
['(2+-2)*2'],
|
['(2+-2)*2'],
|
||||||
|
|
||||||
['sin(10) * cos(50) / min(10, 20/2)'],
|
['sin(10) * cos(50) / min(10, 20/2)'],
|
||||||
|
|
||||||
['100500 * 3.5E5'],
|
['100500 * 3.5E5'],
|
||||||
['100500 * 3.5E-5'],
|
['100500 * 3.5E-5'],
|
||||||
|
|
||||||
|
['-1 + -2'],
|
||||||
|
['-1+-2'],
|
||||||
|
['-1- -2'],
|
||||||
|
['-1/-2'],
|
||||||
|
['-1*-2'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue