aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Wells <bruce.wells@simparel.com>2018-10-30 23:16:01 +0300
committerBruce Wells <bruce.wells@simparel.com>2018-10-31 16:35:40 +0300
commitf0d4562b9ef3bbc64cb8353f0597e639420ede6b (patch)
tree86f215d0c7187843e863e7a980ae81943e7d4a5f
parent334dd26e3c78a36002aed34aa26aa526b11c5dfb (diff)
Division By Zero Exception support
Updated the documentation. Unit tests for strings. DivisionByZeroException support.
-rw-r--r--README.md57
-rw-r--r--src/NXP/Classes/Token/AbstractOperator.php30
-rw-r--r--src/NXP/Classes/Token/TokenDivision.php4
-rw-r--r--src/NXP/Classes/TokenFactory.php33
-rw-r--r--src/NXP/MathExecutor.php23
-rw-r--r--tests/MathTest.php69
6 files changed, 167 insertions, 49 deletions
diff --git a/README.md b/README.md
index 2fdb5c1..f5bf318 100644
--- a/README.md
+++ b/README.md
@@ -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();