diff options
author | Bruce Wells <phpfui@users.noreply.github.com> | 2018-10-31 16:51:19 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-31 16:51:19 +0300 |
commit | 07d02e1fb0aeb9fca7174a065f61a5fa2643b12f (patch) | |
tree | 86f215d0c7187843e863e7a980ae81943e7d4a5f | |
parent | 43f0ff3f28d198fbb4e36346fc5f36fa91cf3e18 (diff) | |
parent | f0d4562b9ef3bbc64cb8353f0597e639420ede6b (diff) |
Merge branch 'master' into master
-rw-r--r-- | README.md | 57 | ||||
-rw-r--r-- | src/NXP/Classes/Token/AbstractOperator.php | 30 | ||||
-rw-r--r-- | src/NXP/Classes/Token/TokenDivision.php | 4 | ||||
-rw-r--r-- | src/NXP/Classes/TokenFactory.php | 33 | ||||
-rw-r--r-- | src/NXP/MathExecutor.php | 23 | ||||
-rw-r--r-- | tests/MathTest.php | 69 |
6 files changed, 167 insertions, 49 deletions
@@ -1,34 +1,37 @@ -[![Stories in Ready](https://badge.waffle.io/NeonXP/MathExecutor.png?label=ready&title=Ready)](https://waffle.io/NeonXP/MathExecutor) -# MathExecutor +# MathExecutor [![Stories in Ready](https://badge.waffle.io/NeonXP/MathExecutor.png?label=ready&title=Ready)](https://waffle.io/NeonXP/MathExecutor) [![Build Status](https://travis-ci.org/NeonXP/MathExecutor.png?branch=master)](https://travis-ci.org/NeonXP/MathExecutor) -[![Build Status](https://travis-ci.org/NeonXP/MathExecutor.png?branch=master)](https://travis-ci.org/NeonXP/MathExecutor) +A simple math expressions calculator -Simple math expressions calculator - -## Install via Composer +## Features: +* Built in support for +, -, *, / and power (^) operators +* Support for user defined operators +* Support for user defined functions +* Unlimited varable length +* String support, as function parameters or as evaluated by PHP +* Exceptions on divide by zero, or treat as zero +* Unary Minus +## Install via Composer: Stable branch ``` composer require "nxp/math-executor" "dev-master" ``` -Dev branch +Dev branch (currently unsupported) ``` composer require "nxp/math-executor" "dev-dev" ``` ## Sample usage: - ```php require "vendor/autoload.php"; -$calculator = new \NXP\MathExecutor(); +$executor = new \NXP\MathExecutor(); -print $calculator->execute("1 + 2 * (2 - (4+10))^2 + sin(10)"); +echo $executor->execute("1 + 2 * (2 - (4+10))^2 + sin(10)"); ``` ## Functions: - Default functions: * sin * cos @@ -48,13 +51,10 @@ $executor->addFunction('abs', function($arg) { ``` ## Operators: - Default operators: `+ - * / ^` Add custom operator to executor: -MyNamespace/ModulusToken.php: - ```php <?php namespace MyNamespace; @@ -113,7 +113,6 @@ $executor->addOperator('MyNamespace\ModulusToken'); ``` ## Variables: - Default variables: ``` @@ -124,9 +123,31 @@ $e = 2.71828182846 You can add own variable to executor: ```php -$executor->setVars(array( +$executor->setVars([ 'var1' => 0.15, 'var2' => 0.22 -)); +]); + +echo $executor->execute("$var1 + $var2"); +``` +## Division By Zero Support: +By default, the result of division by zero is zero and no error is generated. You have the option to thow a \NXP\Exception\DivisionByZeroException by by calling setDivisionByZeroException. -$executor->execute("$var1 + $var2"); +```php +$executor->setDivisionByZeroException(); +try { + echo $executor->execute('1/0'); +} catch (\NXP\Exception\DivisionByZeroException $e) { + echo $e->getMessage(); +} +``` + +## Unary Minus Operator: +Negative numbers are supported via the unary minus operator, but need to have a space before the minus sign. `-1+ -3` is legal, while '`-1+-3` will produce an error due to the way the parser works. Positive numbers are not explicitly supported as unsigned numbers are assumed positive. + +## String Support: +Expressions can contain double or single quoted strings that are evaluated the same way as PHP. You can also pass strings to functions. + +```php +echo $executor->execute("1 + '2.5' * '.5' + myFunction('category')"); +``` diff --git a/src/NXP/Classes/Token/AbstractOperator.php b/src/NXP/Classes/Token/AbstractOperator.php index 6cdfe99..87634d5 100644 --- a/src/NXP/Classes/Token/AbstractOperator.php +++ b/src/NXP/Classes/Token/AbstractOperator.php @@ -17,4 +17,34 @@ abstract class AbstractOperator implements InterfaceToken, InterfaceOperator { const RIGHT_ASSOC = 'RIGHT'; const LEFT_ASSOC = 'LEFT'; + + /** + * Divide by zero reporting + * + * @var bool + */ + private $divideByZeroReporting = false; + + /** + * Set division by zero exception reporting + * + * @param bool $exception default true + * + * @return $this + */ + public function setDivisionByZeroException($exception = true) + { + $this->divideByZeroReporting = $exception; + return $this; + } + + /** + * Get division by zero exception status + * + * @return bool + */ + public function getDivisionByZeroException() + { + return $this->divideByZeroReporting; + } } diff --git a/src/NXP/Classes/Token/TokenDivision.php b/src/NXP/Classes/Token/TokenDivision.php index 5bbc35e..9166d34 100644 --- a/src/NXP/Classes/Token/TokenDivision.php +++ b/src/NXP/Classes/Token/TokenDivision.php @@ -59,6 +59,10 @@ class TokenDivision extends AbstractOperator throw new IncorrectExpressionException("Division requires two operators"); } + if ($this->getDivisionByZeroException() && $op2->getValue() == 0) { + throw new DivisionByZeroException(); + } + $result = $op2->getValue() != 0 ? $op1->getValue() / $op2->getValue() : 0; return new TokenNumber($result); diff --git a/src/NXP/Classes/TokenFactory.php b/src/NXP/Classes/TokenFactory.php index d70dd55..6956f31 100644 --- a/src/NXP/Classes/TokenFactory.php +++ b/src/NXP/Classes/TokenFactory.php @@ -35,6 +35,13 @@ class TokenFactory protected $operators = []; /** + * Divide by zero reporting + * + * @var bool + */ + protected $divideByZeroReporting = false; + + /** * Available functions * * @var array @@ -91,6 +98,29 @@ class TokenFactory } /** + * Set division by zero exception reporting + * + * @param bool $exception default true + * + * @return TokenFactory + */ + public function setDivisionByZeroException($exception = true) + { + $this->divideByZeroReporting = $exception; + return $this; + } + + /** + * Get division by zero exception status + * + * @return bool + */ + public function getDivisionByZeroException() + { + return $this->divideByZeroReporting; + } + + /** * @return string */ public function getTokenParserRegex() @@ -143,7 +173,8 @@ class TokenFactory foreach ($this->operators as $operator) { $regex = sprintf('/%s/i', $operator::getRegex()); if (preg_match($regex, $token)) { - return new $operator; + $op = new $operator; + return $op->setDivisionByZeroException($this->getDivisionByZeroException()); } } diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php index d62d07a..fbf6ab1 100644 --- a/src/NXP/MathExecutor.php +++ b/src/NXP/MathExecutor.php @@ -219,6 +219,29 @@ class MathExecutor } /** + * Set division by zero exception reporting + * + * @param bool $exception default true + * + * @return MathExecutor + */ + public function setDivisionByZeroException($exception = true) + { + $this->tokenFactory->setDivisionByZeroException($exception); + return $this; + } + + /** + * Get division by zero exception status + * + * @return bool + */ + public function getDivisionByZeroException() + { + return $this->tokenFactory->getDivisionByZeroException(); + } + + /** * Execute expression * * @param $expression diff --git a/tests/MathTest.php b/tests/MathTest.php index c8b8a03..c5dd60d 100644 --- a/tests/MathTest.php +++ b/tests/MathTest.php @@ -35,36 +35,6 @@ class MathTest extends \PHPUnit_Framework_TestCase $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() - { - $calculator = new MathExecutor(); - $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() - { - $calculator = new MathExecutor(); - $this->assertEquals($calculator->execute('10 ^ 2'), 100); - } - /** * Expressions data provider */ @@ -94,6 +64,7 @@ class MathTest extends \PHPUnit_Framework_TestCase ['(2- 2)*2'], ['2 + 2*2'], ['2+ 2*2'], + ['2+2*2'], ['(2+2)*2'], ['(2 + 2)*-2'], ['(2+-2)*2'], @@ -103,6 +74,10 @@ class MathTest extends \PHPUnit_Framework_TestCase ['100500 * 3.5E5'], ['100500 * 3.5E-5'], + ['1 + "2" / 3'], + ["1.5 + '2.5' / 4"], + ['1.5 + "2.5" * ".5"'], + ['-1 + -2'], ['-1+-2'], ['-1- -2'], @@ -111,6 +86,40 @@ class MathTest extends \PHPUnit_Framework_TestCase ]; } + 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() + { + $calculator = new MathExecutor(); + $this->assertEquals($calculator->execute('10 / 0'), 0); + } + + public function testZeroDivisionException() + { + $calculator = new MathExecutor(); + $calculator->setDivisionByZeroException(); + $this->expectException(DivisionByZeroException::class); + $calculator->execute('10 / 0'); + } + + public function testExponentiation() + { + $calculator = new MathExecutor(); + $this->assertEquals($calculator->execute('10 ^ 2'), 100); + } + public function testFunction() { $calculator = new MathExecutor(); |