aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md18
-rw-r--r--src/NXP/Classes/Calculator.php2
-rw-r--r--src/NXP/Classes/CustomFunction.php4
-rw-r--r--src/NXP/Classes/Operator.php2
-rw-r--r--src/NXP/Classes/Tokenizer.php20
-rw-r--r--src/NXP/MathExecutor.php143
-rw-r--r--tests/MathTest.php9
7 files changed, 157 insertions, 41 deletions
diff --git a/README.md b/README.md
index c665575..65b9dc2 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ $executor->addOperator(new Operator(
$op2 = array_pop($stack);
$op1 = array_pop($stack);
$result = $op1->getValue() % $op2->getValue();
-
+
return $result;
}
));
@@ -124,15 +124,12 @@ $e = 2.71828182846
You can add your own variables to executor:
```php
-$executor->setVars([
- 'var1' => 0.15,
- 'var2' => 0.22
-]);
+$executor->setVar('var1', 0.15)->setVar('var2', 0.22);
echo $executor->execute("$var1 + $var2");
```
## Division By Zero Support:
-Division by zero throws a `\NXP\Exception\DivisionByZeroException`
+Division by zero throws a `\NXP\Exception\DivisionByZeroException` by default
```php
try {
echo $executor->execute('1/0');
@@ -140,12 +137,15 @@ try {
echo $e->getMessage();
}
```
-If you want another behavior, you should override division operator:
-
+Or call setDivisionByZeroIsZero
+```php
+echo $executor->setDivisionByZeroIsZero()->execute('1/0');
+```
+If you want another behavior, you can override division operator:
```php
$executor->addOperator("/", false, 180, function($a, $b) {
if ($b == 0) {
- return 0;
+ return null;
}
return $a / $b;
});
diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php
index 0db49e1..c6ccff1 100644
--- a/src/NXP/Classes/Calculator.php
+++ b/src/NXP/Classes/Calculator.php
@@ -49,7 +49,7 @@ class Calculator
* @throws IncorrectExpressionException
* @throws UnknownVariableException
*/
- public function calculate($tokens, $variables)
+ public function calculate(array $tokens, array $variables)
{
/** @var Token[] $stack */
$stack = [];
diff --git a/src/NXP/Classes/CustomFunction.php b/src/NXP/Classes/CustomFunction.php
index 944f7d9..bf36405 100644
--- a/src/NXP/Classes/CustomFunction.php
+++ b/src/NXP/Classes/CustomFunction.php
@@ -32,7 +32,7 @@ class CustomFunction
* @param int $places
* @throws ReflectionException
*/
- public function __construct(string $name, callable $function, $places = null)
+ public function __construct(string $name, callable $function, ?int $places = null)
{
$this->name = $name;
$this->function = $function;
@@ -44,7 +44,7 @@ class CustomFunction
}
}
- public function execute(&$stack)
+ public function execute(array &$stack) : Token
{
if (count($stack) < $this->places) {
throw new IncorrectExpressionException();
diff --git a/src/NXP/Classes/Operator.php b/src/NXP/Classes/Operator.php
index 53550a9..c3762ea 100644
--- a/src/NXP/Classes/Operator.php
+++ b/src/NXP/Classes/Operator.php
@@ -52,7 +52,7 @@ class Operator
$this->places = $reflection->getNumberOfParameters();
}
- public function execute(&$stack)
+ public function execute(array &$stack) : Token
{
if (count($stack) < $this->places) {
throw new IncorrectExpressionException();
diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php
index b72b869..caf395f 100644
--- a/src/NXP/Classes/Tokenizer.php
+++ b/src/NXP/Classes/Tokenizer.php
@@ -66,7 +66,7 @@ class Tokenizer
$this->operators = $operators;
}
- public function tokenize()
+ public function tokenize() : self
{
foreach (str_split($this->input, 1) as $ch) {
switch (true) {
@@ -173,17 +173,17 @@ class Tokenizer
return $this;
}
- private function isNumber($ch)
+ private function isNumber(string $ch) : bool
{
return $ch >= '0' && $ch <= '9';
}
- private function isAlpha($ch)
+ private function isAlpha(string $ch) : bool
{
return $ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || $ch == '_';
}
- private function emptyNumberBufferAsLiteral()
+ private function emptyNumberBufferAsLiteral() : void
{
if ($this->numberBuffer != "") {
$this->tokens[] = new Token(Token::Literal, $this->numberBuffer);
@@ -191,22 +191,22 @@ class Tokenizer
}
}
- private function isDot($ch)
+ private function isDot(string $ch) : bool
{
return $ch == '.';
}
- private function isLP($ch)
+ private function isLP(string $ch) : bool
{
return $ch == '(';
}
- private function isRP($ch)
+ private function isRP(string $ch) : bool
{
return $ch == ')';
}
- private function emptyStrBufferAsVariable()
+ private function emptyStrBufferAsVariable() : void
{
if ($this->stringBuffer != "") {
$this->tokens[] = new Token(Token::Variable, $this->stringBuffer);
@@ -214,7 +214,7 @@ class Tokenizer
}
}
- private function isComma($ch)
+ private function isComma(string $ch) : bool
{
return $ch == ',';
}
@@ -224,7 +224,7 @@ class Tokenizer
* @throws IncorrectBracketsException
* @throws UnknownOperatorException
*/
- public function buildReversePolishNotation()
+ public function buildReversePolishNotation() : array
{
$tokens = [];
/** @var SplStack<Token> $stack */
diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php
index a0a3f9b..5fdc78f 100644
--- a/src/NXP/MathExecutor.php
+++ b/src/NXP/MathExecutor.php
@@ -15,6 +15,7 @@ use NXP\Classes\Calculator;
use NXP\Classes\CustomFunction;
use NXP\Classes\Operator;
use NXP\Classes\Tokenizer;
+use NXP\MathExecutorException;
use NXP\Exception\DivisionByZeroException;
use ReflectionException;
@@ -34,12 +35,12 @@ class MathExecutor
/**
* @var Operator[]
*/
- public $operators = [];
+ private $operators = [];
/**
* @var CustomFunction[]
*/
- public $functions = [];
+ private $functions = [];
/**
* @var array
@@ -58,10 +59,10 @@ class MathExecutor
* Set default operands and functions
* @throws ReflectionException
*/
- protected function addDefaults()
+ protected function addDefaults() : void
{
foreach ($this->defaultOperators() as $name => $operator) {
- list($callable, $priority, $isRightAssoc) = $operator;
+ [$callable, $priority, $isRightAssoc] = $operator;
$this->addOperator(new Operator($name, $isRightAssoc, $priority, $callable));
}
foreach ($this->defaultFunctions() as $name => $callable) {
@@ -75,7 +76,7 @@ class MathExecutor
*
* @return array of class names
*/
- protected function defaultOperators()
+ protected function defaultOperators() : array
{
return [
'+' => [
@@ -181,7 +182,7 @@ class MathExecutor
* @param Operator $operator
* @return MathExecutor
*/
- public function addOperator(Operator $operator)
+ public function addOperator(Operator $operator) : self
{
$this->operators[$operator->operator] = $operator;
return $this;
@@ -193,7 +194,7 @@ class MathExecutor
*
* @return array
*/
- protected function defaultFunctions()
+ protected function defaultFunctions() : array
{
return [
'abs' => function ($arg) {
@@ -341,9 +342,9 @@ class MathExecutor
* @throws Exception\UnknownOperatorException
* @throws Exception\UnknownVariableException
*/
- public function execute($expression)
+ public function execute(string $expression)
{
- $cachekey = (string)$expression;
+ $cachekey = $expression;
if (!array_key_exists($cachekey, $this->cache)) {
$tokens = (new Tokenizer($expression, $this->operators))->tokenize()->buildReversePolishNotation();
$this->cache[$cachekey] = $tokens;
@@ -363,7 +364,7 @@ class MathExecutor
* @return MathExecutor
* @throws ReflectionException
*/
- public function addFunction($name, $function = null, $places = null)
+ public function addFunction(string $name, ?callable $function = null, ?int $places = null) : self
{
$this->functions[$name] = new CustomFunction($name, $function, $places);
return $this;
@@ -374,7 +375,7 @@ class MathExecutor
*
* @return array
*/
- protected function defaultVars()
+ protected function defaultVars() : array
{
return [
'pi' => 3.14159265359,
@@ -382,6 +383,126 @@ class MathExecutor
];
}
+ /**
+ * Get all vars
+ *
+ * @return array
+ */
+ public function getVars() : array
+ {
+ return $this->variables;
+ }
+
+ /**
+ * Get a specific var
+ *
+ * @param string $variable
+ * @return integer|float
+ * @throws UnknownVariableException
+ */
+ public function getVar(string $variable)
+ {
+ if (!isset($this->variables[$variable])) {
+ throw new UnknownVariableException("Variable ({$variable}) not set");
+ }
+ return $this->variables[$variable];
+ }
+
+ /**
+ * Add variable to executor
+ *
+ * @param string $variable
+ * @param integer|float $value
+ * @return MathExecutor
+ * @throws MathExecutorException
+ */
+ public function setVar(string $variable, $value) : self
+ {
+ if (!is_numeric($value)) {
+ throw new MathExecutorException("Variable ({$variable}) value must be a number ({$value}) type ({gettype($value)})");
+ }
+ $this->variables[$variable] = $value;
+ return $this;
+ }
+
+ /**
+ * Add variables to executor
+ *
+ * @param array $variables
+ * @param bool $clear Clear previous variables
+ * @return MathExecutor
+ * @throws \Exception
+ */
+ public function setVars(array $variables, bool $clear = true) : self
+ {
+ if ($clear) {
+ $this->removeVars();
+ }
+ foreach ($variables as $name => $value) {
+ $this->setVar($name, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Remove variable from executor
+ *
+ * @param string $variable
+ * @return MathExecutor
+ */
+ public function removeVar(string $variable) : self
+ {
+ unset ($this->variables[$variable]);
+ return $this;
+ }
+
+ /**
+ * Remove all variables
+ * @return MathExecutor
+ */
+ public function removeVars() : self
+ {
+ $this->variables = [];
+ return $this;
+ }
+
+ /**
+ * Get all registered operators to executor
+ *
+ * @return array of operator class names
+ */
+ public function getOperators()
+ {
+ return $this->tokenFactory->getOperators();
+ }
+
+ /**
+ * Get all registered functions
+ *
+ * @return array containing callback and places indexed by
+ * function name
+ */
+ public function getFunctions() : array
+ {
+ return $this->tokenFactory->getFunctions();
+ }
+
+ /**
+ * Set division by zero returns zero instead of throwing DivisionByZeroException
+ *
+ * @return MathExecutor
+ */
+ public function setDivisionByZeroIsZero() : self
+ {
+ $this->addOperator(new Operator("/", false, 180, function ($a, $b) {
+ if ($b == 0) {
+ return 0;
+ }
+ return $a / $b;
+ }));
+ return $this;
+ }
+
public function __clone()
{
$this->addDefaults();
diff --git a/tests/MathTest.php b/tests/MathTest.php
index 958f14c..39ac649 100644
--- a/tests/MathTest.php
+++ b/tests/MathTest.php
@@ -203,7 +203,7 @@ class MathTest extends TestCase
['(-3 * -1)'],
['1 + (-3 * -1)'],
['1 + ( -3 * 1)'],
- ['1 + (3 * -1)'],
+ ['1 + (3 *-1)'],
['1 - 0'],
['1-0'],
];
@@ -226,12 +226,7 @@ class MathTest extends TestCase
public function testZeroDivision()
{
$calculator = new MathExecutor();
- $calculator->addOperator(new Operator("/", false, 180, function ($a, $b) {
- if ($b == 0) {
- return 0;
- }
- return $a / $b;
- }));
+ $calculator->setDivisionByZeroIsZero();
$this->assertEquals(0, $calculator->execute('10 / 0'));
}