aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md84
-rw-r--r--composer.json51
-rw-r--r--src/NXP/Classes/Calculator.php65
-rw-r--r--src/NXP/Classes/CustomFunction.php63
-rw-r--r--src/NXP/Classes/Lexer.php161
-rw-r--r--src/NXP/Classes/Operator.php69
-rw-r--r--src/NXP/Classes/Token.php35
-rw-r--r--src/NXP/Classes/Token/AbstractContainerToken.php46
-rw-r--r--src/NXP/Classes/Token/AbstractOperator.php51
-rw-r--r--src/NXP/Classes/Token/InterfaceFunction.php23
-rw-r--r--src/NXP/Classes/Token/InterfaceOperator.php33
-rw-r--r--src/NXP/Classes/Token/InterfaceToken.php22
-rw-r--r--src/NXP/Classes/Token/TokenAnd.php53
-rw-r--r--src/NXP/Classes/Token/TokenComma.php53
-rw-r--r--src/NXP/Classes/Token/TokenDegree.php64
-rw-r--r--src/NXP/Classes/Token/TokenDivision.php70
-rw-r--r--src/NXP/Classes/Token/TokenEqual.php53
-rw-r--r--src/NXP/Classes/Token/TokenFunction.php42
-rw-r--r--src/NXP/Classes/Token/TokenGreaterThan.php53
-rw-r--r--src/NXP/Classes/Token/TokenGreaterThanOrEqual.php53
-rw-r--r--src/NXP/Classes/Token/TokenLeftBracket.php25
-rw-r--r--src/NXP/Classes/Token/TokenLessThan.php53
-rw-r--r--src/NXP/Classes/Token/TokenLessThanOrEqual.php53
-rw-r--r--src/NXP/Classes/Token/TokenMinus.php68
-rw-r--r--src/NXP/Classes/Token/TokenMultiply.php64
-rw-r--r--src/NXP/Classes/Token/TokenNotEqual.php53
-rw-r--r--src/NXP/Classes/Token/TokenNumber.php25
-rw-r--r--src/NXP/Classes/Token/TokenOr.php53
-rw-r--r--src/NXP/Classes/Token/TokenPlus.php64
-rw-r--r--src/NXP/Classes/Token/TokenRightBracket.php25
-rw-r--r--src/NXP/Classes/Token/TokenStringDoubleQuoted.php25
-rw-r--r--src/NXP/Classes/Token/TokenStringSingleQuoted.php26
-rw-r--r--src/NXP/Classes/Token/TokenVariable.php25
-rw-r--r--src/NXP/Classes/TokenFactory.php217
-rw-r--r--src/NXP/Classes/Tokenizer.php302
-rw-r--r--src/NXP/Exception/UnknownTokenException.php19
-rw-r--r--src/NXP/MathExecutor.php386
-rw-r--r--tests/MathTest.php59
38 files changed, 777 insertions, 1909 deletions
diff --git a/README.md b/README.md
index 3ed5a95..c665575 100644
--- a/README.md
+++ b/README.md
@@ -23,9 +23,9 @@ composer require nxp/math-executor
## Sample usage:
```php
-require 'vendor/autoload.php';
+use NXP\MathExecutor;
-$executor = new \NXP\MathExecutor();
+$executor = new MathExecutor();
echo $executor->execute('1 + 2 * (2 - (4+10))^2 + sin(10)');
```
@@ -84,60 +84,21 @@ Default operators: `+ - * / ^`
Add custom operator to executor:
```php
-<?php
-namespace MyNamespace;
-
-use NXP\Classes\Token\AbstractOperator;
-
-class ModulusToken extends AbstractOperator
-{
- /**
- * Regex of this operator
- * @return string
- */
- public static function getRegex()
- {
- return '\%';
- }
-
- /**
- * Priority of this operator
- * @return int
- */
- public function getPriority()
- {
- return 180;
- }
+use NXP\Classes\Operator;
- /**
- * Associaion of this operator (self::LEFT_ASSOC or self::RIGHT_ASSOC)
- * @return string
- */
- public function getAssociation()
+$executor->addOperator(new Operator(
+ '%', // Operator sign
+ false, // Is right associated operator
+ 170, // Operator priority
+ function (&$stack)
{
- return self::LEFT_ASSOC;
+ $op2 = array_pop($stack);
+ $op1 = array_pop($stack);
+ $result = $op1->getValue() % $op2->getValue();
+
+ return $result;
}
-
- /**
- * Execution of this operator
- * @param InterfaceToken[] $stack Stack of tokens
- * @return TokenNumber Result of execution
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
- $result = $op1->getValue() % $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
-```
-
-And adding to executor:
-
-```php
-$executor->addOperator('MyNamespace\ModulusToken');
+));
```
## Logical operators:
@@ -171,16 +132,25 @@ $executor->setVars([
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 throw a `\NXP\Exception\DivisionByZeroException` by calling `setDivisionByZeroException`.
-
+Division by zero throws a `\NXP\Exception\DivisionByZeroException`
```php
-$executor->setDivisionByZeroException();
try {
echo $executor->execute('1/0');
-} catch (\NXP\Exception\DivisionByZeroException $e) {
+} catch (DivisionByZeroException $e) {
echo $e->getMessage();
}
```
+If you want another behavior, you should override division operator:
+
+```php
+$executor->addOperator("/", false, 180, function($a, $b) {
+ if ($b == 0) {
+ return 0;
+ }
+ return $a / $b;
+});
+echo $executor->execute('1/0');
+```
## Unary Minus Operator:
Negative numbers are supported via the unary minus operator. Positive numbers are not explicitly supported as unsigned numbers are assumed positive.
diff --git a/composer.json b/composer.json
index 8a5bd99..3098443 100644
--- a/composer.json
+++ b/composer.json
@@ -1,23 +1,36 @@
{
- "name": "nxp/math-executor",
- "description": "Simple math expressions calculator",
- "minimum-stability": "stable",
- "keywords": ["math","parser","expression","calculator","formula","mathmatics"],
- "homepage": "http://github.com/NeonXP/MathExecutor",
- "license": "MIT",
- "authors": [
- {
- "name": "Alexander 'NeonXP' Kiryukhin",
- "email": "frei@neonxp.info"
- }
- ],
- "require": {
- "php": ">=7.1"
+ "name": "nxp/math-executor",
+ "description": "Simple math expressions calculator",
+ "minimum-stability": "stable",
+ "keywords": [
+ "math",
+ "parser",
+ "expression",
+ "calculator",
+ "formula",
+ "mathmatics"
+ ],
+ "homepage": "http://github.com/NeonXP/MathExecutor",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Alexander 'NeonXP' Kiryukhin",
+ "email": "a.kiryukhin@mail.ru"
},
- "require-dev": {
- "phpunit/phpunit": "~7.0"
- },
- "autoload": {
- "psr-0": {"NXP": "src/"}
+ {
+ "name": "Bruce Wells",
+ "email": "brucekwells@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~7.0"
+ },
+ "autoload": {
+ "psr-0": {
+ "NXP": "src/"
}
+ }
}
diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php
index 7d82ecd..0db49e1 100644
--- a/src/NXP/Classes/Calculator.php
+++ b/src/NXP/Classes/Calculator.php
@@ -10,13 +10,9 @@
namespace NXP\Classes;
-use NXP\Classes\Token\InterfaceOperator;
-use NXP\Classes\Token\TokenFunction;
-use NXP\Classes\Token\TokenNumber;
-use NXP\Classes\Token\TokenStringSingleQuoted;
-use NXP\Classes\Token\TokenStringDoubleQuoted;
-use NXP\Classes\Token\TokenVariable;
use NXP\Exception\IncorrectExpressionException;
+use NXP\Exception\UnknownFunctionException;
+use NXP\Exception\UnknownOperatorException;
use NXP\Exception\UnknownVariableException;
/**
@@ -25,35 +21,64 @@ use NXP\Exception\UnknownVariableException;
class Calculator
{
/**
+ * @var CustomFunction[]
+ */
+ private $functions;
+
+ /**
+ * @var Operator[]
+ */
+ private $operators;
+
+ /**
+ * Calculator constructor.
+ * @param CustomFunction[] $functions
+ * @param Operator[] $operators
+ */
+ public function __construct(array $functions, array $operators)
+ {
+ $this->functions = $functions;
+ $this->operators = $operators;
+ }
+
+ /**
* Calculate array of tokens in reverse polish notation
- * @param array $tokens
- * @param array $variables
- * @return number Result
- * @throws \NXP\Exception\IncorrectExpressionException
- * @throws \NXP\Exception\UnknownVariableException
+ * @param Token[] $tokens
+ * @param array $variables
+ * @return mixed
+ * @throws IncorrectExpressionException
+ * @throws UnknownVariableException
*/
public function calculate($tokens, $variables)
{
+ /** @var Token[] $stack */
$stack = [];
foreach ($tokens as $token) {
- if ($token instanceof TokenNumber || $token instanceof TokenStringDoubleQuoted || $token instanceof TokenStringSingleQuoted) {
+ if ($token->type === Token::Literal || $token->type === Token::String) {
$stack[] = $token;
- } else if ($token instanceof TokenVariable) {
- $variable = $token->getValue();
+ } else if ($token->type === Token::Variable) {
+ $variable = $token->value;
if (!array_key_exists($variable, $variables)) {
throw new UnknownVariableException($variable);
}
$value = $variables[$variable];
- $stack[] = new TokenNumber($value);
- } else if ($token instanceof InterfaceOperator || $token instanceof TokenFunction) {
- $stack[] = $token->execute($stack);
+ $stack[] = new Token(Token::Literal, $value);
+ } else if ($token->type === Token::Function) {
+ if (!array_key_exists($token->value, $this->functions)) {
+ throw new UnknownFunctionException($token->value);
+ }
+ $stack[] = $this->functions[$token->value]->execute($stack);
+ } elseif ($token->type === Token::Operator) {
+ if (!array_key_exists($token->value, $this->operators)) {
+ throw new UnknownOperatorException($token->value);
+ }
+ $stack[] = $this->operators[$token->value]->execute($stack);
}
}
$result = array_pop($stack);
- if ($result === null || ! empty($stack)) {
+ if ($result === null || !empty($stack)) {
throw new IncorrectExpressionException();
}
-
- return $result->getValue();
+ return $result->value;
}
}
diff --git a/src/NXP/Classes/CustomFunction.php b/src/NXP/Classes/CustomFunction.php
new file mode 100644
index 0000000..944f7d9
--- /dev/null
+++ b/src/NXP/Classes/CustomFunction.php
@@ -0,0 +1,63 @@
+<?php
+
+
+namespace NXP\Classes;
+
+
+use NXP\Exception\IncorrectExpressionException;
+use ReflectionException;
+use ReflectionFunction;
+
+class CustomFunction
+{
+ /**
+ * @var string
+ */
+ public $name;
+
+ /**
+ * @var callable $function
+ */
+ public $function;
+
+ /**
+ * @var int
+ */
+ public $places;
+
+ /**
+ * CustomFunction constructor.
+ * @param string $name
+ * @param callable $function
+ * @param int $places
+ * @throws ReflectionException
+ */
+ public function __construct(string $name, callable $function, $places = null)
+ {
+ $this->name = $name;
+ $this->function = $function;
+ if ($places === null) {
+ $reflection = new ReflectionFunction($function);
+ $this->places = $reflection->getNumberOfParameters();
+ } else {
+ $this->places = $places;
+ }
+ }
+
+ public function execute(&$stack)
+ {
+ if (count($stack) < $this->places) {
+ throw new IncorrectExpressionException();
+ }
+ $args = [];
+ for ($i = 0; $i < $this->places; $i++) {
+ array_unshift($args, array_pop($stack)->value);
+ }
+
+ $result = call_user_func_array($this->function, $args);
+
+ return new Token(Token::Literal, $result);
+ }
+
+
+} \ No newline at end of file
diff --git a/src/NXP/Classes/Lexer.php b/src/NXP/Classes/Lexer.php
deleted file mode 100644
index 9964de9..0000000
--- a/src/NXP/Classes/Lexer.php
+++ /dev/null
@@ -1,161 +0,0 @@
-<?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\Classes;
-
-use NXP\Classes\Token\AbstractOperator;
-use NXP\Classes\Token\InterfaceOperator;
-use NXP\Classes\Token\TokenComma;
-use NXP\Classes\Token\TokenFunction;
-use NXP\Classes\Token\TokenLeftBracket;
-use NXP\Classes\Token\TokenMinus;
-use NXP\Classes\Token\TokenNumber;
-use NXP\Classes\Token\TokenRightBracket;
-use NXP\Classes\Token\TokenStringDoubleQuoted;
-use NXP\Classes\Token\TokenStringSingleQuoted;
-use NXP\Classes\Token\TokenVariable;
-use NXP\Exception\IncorrectBracketsException;
-use NXP\Exception\IncorrectExpressionException;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class Lexer
-{
- /**
- * @var TokenFactory
- */
- private $tokenFactory;
-
- public function __construct($tokenFactory)
- {
- $this->tokenFactory = $tokenFactory;
- }
-
- /**
- * @param string $input Source string of equation
- * @return array Tokens stream
- */
- public function stringToTokensStream($input)
- {
- $matches = [];
- preg_match_all($this->tokenFactory->getTokenParserRegex(), $input, $matches);
- $tokenFactory = $this->tokenFactory;
- $tokensStream = array_map(
- function ($token) use ($tokenFactory) {
- return $tokenFactory->createToken($token);
- },
- $matches[0]
- );
-
- return $tokensStream;
- }
-
- /**
- * @param array $tokensStream Tokens stream
- * @return array Array of tokens in revers polish notation
- * @throws IncorrectBracketsException
- */
- public function buildReversePolishNotation($tokensStream)
- {
- $output = [];
- $stack = [];
- $lastToken = null;
-
- foreach ($tokensStream as $token) {
- if ($token instanceof TokenStringDoubleQuoted) {
- $output[] = $token;
- } elseif ($token instanceof TokenStringSingleQuoted) {
- $output[] = $token;
- } elseif ($token instanceof TokenNumber) {
- // if the number starts with a minus sign, it could be a negative number, or it could be an operator grabbed by the greedy regex
- // if previous token is an operator or open bracket, then it negative, otherwise remove the minus sign and put a negative operator on the stack
- if ($lastToken !== null) {
- $value = $token->getValue();
- if (($value < 0 || $this->isNegativeZero($value)) && ! ($lastToken instanceof AbstractOperator || $lastToken instanceof TokenLeftBracket)) {
- $token = new TokenNumber(abs($value));
- $output[] = $token;
- $output[] = new TokenMinus('-');
- } else {
- $output[] = $token;
- }
- } else {
- $output[] = $token;
- }
- } elseif ($token instanceof TokenVariable) {
- $output[] = $token;
- } elseif ($token instanceof TokenFunction) {
- $stack[] = $token;
- } elseif ($token instanceof AbstractOperator) {
- // While we have something on the stack
- while (($count = count($stack)) > 0
- && (
- // If it is a function
- ($stack[$count - 1] instanceof TokenFunction)
-
- ||
- // Or the operator at the top of the operator stack
- // has (left associative and equal precedence)
- // or has greater precedence
- (($stack[$count - 1] instanceof InterfaceOperator) &&
- (
- ($stack[$count - 1]->getAssociation() == AbstractOperator::LEFT_ASSOC &&
- $token->getPriority() == $stack[$count - 1]->getPriority())
- ||
- ($stack[$count - 1]->getPriority() > $token->getPriority())
- )
- )
- )
-
- // And not a left bracket
- && (!($stack[$count - 1] instanceof TokenLeftBracket))) {
- $output[] = array_pop($stack);
- }
-
- // Comma operators do nothing really, don't put them on the stack
- if (! ($token instanceof TokenComma)) {
- $stack[] = $token;
- }
- } elseif ($token instanceof TokenLeftBracket) {
- $stack[] = $token;
- } elseif ($token instanceof TokenRightBracket) {
- while (($current = array_pop($stack)) && (!($current instanceof TokenLeftBracket))) {
- $output[] = $current;
- }
- if (!empty($stack) && ($stack[count($stack) - 1] instanceof TokenFunction)) {
- $output[] = array_pop($stack);
- }
- }
- $lastToken = $token;
- }
- while (!empty($stack)) {
- $token = array_pop($stack);
- if ($token instanceof TokenLeftBracket || $token instanceof TokenRightBracket) {
- throw new IncorrectBracketsException();
- }
- $output[] = $token;
- }
-
- return $output;
- }
-
- /**
- * Check if the value is a negative zero
- *
- * @param int|float $x The value to check
- * @return boolean True if negative zero, false otherwise
- */
- private function isNegativeZero($x)
- {
- $floatVal = floatval($x);
-
- return $floatVal === 0.0 && $floatVal ** -1 === -INF;
- }
-}
diff --git a/src/NXP/Classes/Operator.php b/src/NXP/Classes/Operator.php
new file mode 100644
index 0000000..53550a9
--- /dev/null
+++ b/src/NXP/Classes/Operator.php
@@ -0,0 +1,69 @@
+<?php
+
+
+namespace NXP\Classes;
+
+
+use NXP\Exception\IncorrectExpressionException;
+use ReflectionFunction;
+
+class Operator
+{
+ /**
+ * @var string
+ */
+ public $operator;
+
+ /**
+ * @var bool
+ */
+ public $isRightAssoc;
+
+ /**
+ * @var int
+ */
+ public $priority;
+
+ /**
+ * @var callable<\SplStack>
+ */
+ public $function;
+
+ /**
+ * @var int
+ */
+ public $places;
+
+ /**
+ * Operator constructor.
+ * @param string $operator
+ * @param bool $isRightAssoc
+ * @param int $priority
+ * @param callable $function
+ */
+ public function __construct(string $operator, bool $isRightAssoc, int $priority, callable $function)
+ {
+ $this->operator = $operator;
+ $this->isRightAssoc = $isRightAssoc;
+ $this->priority = $priority;
+ $this->function = $function;
+ $this->function = $function;
+ $reflection = new ReflectionFunction($function);
+ $this->places = $reflection->getNumberOfParameters();
+ }
+
+ public function execute(&$stack)
+ {
+ if (count($stack) < $this->places) {
+ throw new IncorrectExpressionException();
+ }
+ $args = [];
+ for ($i = 0; $i < $this->places; $i++) {
+ array_unshift($args, array_pop($stack)->value);
+ }
+
+ $result = call_user_func_array($this->function, $args);
+
+ return new Token(Token::Literal, $result);
+ }
+} \ No newline at end of file
diff --git a/src/NXP/Classes/Token.php b/src/NXP/Classes/Token.php
new file mode 100644
index 0000000..49bf741
--- /dev/null
+++ b/src/NXP/Classes/Token.php
@@ -0,0 +1,35 @@
+<?php
+
+
+namespace NXP\Classes;
+
+class Token
+{
+ const Literal = "literal";
+ const Variable = "variable";
+ const Operator = "operator";
+ const LeftParenthesis = "LP";
+ const RightParenthesis = "RP";
+ const Function = "function";
+ const ParamSeparator = "separator";
+ const String = "string";
+ const Space = "space";
+
+ public $type = self::Literal;
+
+ /**
+ * @var float|string
+ */
+ public $value;
+
+ /**
+ * Token constructor.
+ * @param string $type
+ * @param float|string $value
+ */
+ public function __construct(string $type, $value)
+ {
+ $this->type = $type;
+ $this->value = $value;
+ }
+}
diff --git a/src/NXP/Classes/Token/AbstractContainerToken.php b/src/NXP/Classes/Token/AbstractContainerToken.php
deleted file mode 100644
index be2da5f..0000000
--- a/src/NXP/Classes/Token/AbstractContainerToken.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-abstract class AbstractContainerToken implements InterfaceToken
-{
- /**
- * @var string
- */
- protected $value;
-
- /**
- * @param string $value
- */
- public function __construct($value)
- {
- $this->value = $value;
- }
-
- /**
- * @param string $value
- */
- public function setValue($value)
- {
- $this->value = $value;
- }
-
- /**
- * @return string
- */
- public function getValue()
- {
- return $this->value;
- }
-}
diff --git a/src/NXP/Classes/Token/AbstractOperator.php b/src/NXP/Classes/Token/AbstractOperator.php
deleted file mode 100644
index 67e8031..0000000
--- a/src/NXP/Classes/Token/AbstractOperator.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-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/InterfaceFunction.php b/src/NXP/Classes/Token/InterfaceFunction.php
deleted file mode 100644
index a457d0e..0000000
--- a/src/NXP/Classes/Token/InterfaceFunction.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-interface InterfaceFunction
-{
- /**
- * @param array $stack
- * @return $this
- */
- public function execute(&$stack);
-}
diff --git a/src/NXP/Classes/Token/InterfaceOperator.php b/src/NXP/Classes/Token/InterfaceOperator.php
deleted file mode 100644
index 9e3bae1..0000000
--- a/src/NXP/Classes/Token/InterfaceOperator.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-interface InterfaceOperator
-{
- /**
- * @return int
- */
- public function getPriority();
-
- /**
- * @return string
- */
- public function getAssociation();
-
- /**
- * @param array $stack
- * @return TokenNumber
- */
- public function execute(&$stack);
-}
diff --git a/src/NXP/Classes/Token/InterfaceToken.php b/src/NXP/Classes/Token/InterfaceToken.php
deleted file mode 100644
index db07aeb..0000000
--- a/src/NXP/Classes/Token/InterfaceToken.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-interface InterfaceToken
-{
- /**
- * @return string
- */
- public static function getRegex();
-}
diff --git a/src/NXP/Classes/Token/TokenAnd.php b/src/NXP/Classes/Token/TokenAnd.php
deleted file mode 100644
index dab4497..0000000
--- a/src/NXP/Classes/Token/TokenAnd.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenAnd extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '&&';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 100;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("&& requires two operators");
- }
-
- $result = $op1->getValue() && $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenComma.php b/src/NXP/Classes/Token/TokenComma.php
deleted file mode 100644
index f6fc068..0000000
--- a/src/NXP/Classes/Token/TokenComma.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenComma extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\,';
- }
-
- /**
- * Comma operator is lowest priority
- *
- * @return int
- */
- public function getPriority()
- {
- return 0;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param array $stack
- * @return TokenNumber
- */
- public function execute(&$stack)
- {
- // Comma operators don't do anything, stack has already executed
- }
-
-}
diff --git a/src/NXP/Classes/Token/TokenDegree.php b/src/NXP/Classes/Token/TokenDegree.php
deleted file mode 100644
index 0d22f91..0000000
--- a/src/NXP/Classes/Token/TokenDegree.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?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\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-/**
-* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
-*/
-class TokenDegree extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\^';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 220;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::RIGHT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return TokenNumber
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = 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();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenDivision.php b/src/NXP/Classes/Token/TokenDivision.php
deleted file mode 100644
index 328833b..0000000
--- a/src/NXP/Classes/Token/TokenDivision.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-use NXP\Exception\DivisionByZeroException;
-
-/**
-* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
-*/
-class TokenDivision extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\/';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 180;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- * @throws \NXP\Exception\DivisionByZeroException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- 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/Token/TokenEqual.php b/src/NXP/Classes/Token/TokenEqual.php
deleted file mode 100644
index b0ac31e..0000000
--- a/src/NXP/Classes/Token/TokenEqual.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenEqual extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\=\=';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 140;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("== requires two operators");
- }
-
- $result = $op1->getValue() == $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenFunction.php b/src/NXP/Classes/Token/TokenFunction.php
deleted file mode 100644
index 432f107..0000000
--- a/src/NXP/Classes/Token/TokenFunction.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenFunction extends AbstractContainerToken implements InterfaceFunction
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
- }
-
- /**
- * @param array $stack
- * @return $this
- */
- public function execute(&$stack)
- {
- $args = [];
- list($places, $function) = $this->value;
- for ($i = 0; $i < $places; $i++) {
- array_unshift($args, array_pop($stack)->getValue());
- }
-
- $result = call_user_func_array($function, $args);
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenGreaterThan.php b/src/NXP/Classes/Token/TokenGreaterThan.php
deleted file mode 100644
index 51a5aca..0000000
--- a/src/NXP/Classes/Token/TokenGreaterThan.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenGreaterThan extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '>';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 150;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("> requires two operators");
- }
-
- $result = $op1->getValue() > $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenGreaterThanOrEqual.php b/src/NXP/Classes/Token/TokenGreaterThanOrEqual.php
deleted file mode 100644
index aa4425f..0000000
--- a/src/NXP/Classes/Token/TokenGreaterThanOrEqual.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenGreaterThanOrEqual extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '>\=';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 150;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException(">= requires two operators");
- }
-
- $result = $op1->getValue() >= $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenLeftBracket.php b/src/NXP/Classes/Token/TokenLeftBracket.php
deleted file mode 100644
index 08165d8..0000000
--- a/src/NXP/Classes/Token/TokenLeftBracket.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenLeftBracket implements InterfaceToken
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\(';
- }
-}
diff --git a/src/NXP/Classes/Token/TokenLessThan.php b/src/NXP/Classes/Token/TokenLessThan.php
deleted file mode 100644
index d289028..0000000
--- a/src/NXP/Classes/Token/TokenLessThan.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenLessThan extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '<';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 150;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("< requires two operators");
- }
-
- $result = $op1->getValue() < $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenLessThanOrEqual.php b/src/NXP/Classes/Token/TokenLessThanOrEqual.php
deleted file mode 100644
index 5a2fba3..0000000
--- a/src/NXP/Classes/Token/TokenLessThanOrEqual.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenLessThanOrEqual extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '<\=';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 150;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("<= requires two operators");
- }
-
- $result = $op1->getValue() <= $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenMinus.php b/src/NXP/Classes/Token/TokenMinus.php
deleted file mode 100644
index b4b04e3..0000000
--- a/src/NXP/Classes/Token/TokenMinus.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?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\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-/**
-* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
-*/
-class TokenMinus extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\-';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 170;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op2 === null) {
- throw new IncorrectExpressionException("Subtraction requires right operator");
- }
-
- if (!$op1) {
- $op1 = new TokenNumber(0);
- }
-
- $result = $op1->getValue() - $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenMultiply.php b/src/NXP/Classes/Token/TokenMultiply.php
deleted file mode 100644
index 762fb48..0000000
--- a/src/NXP/Classes/Token/TokenMultiply.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?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\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-/**
-* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
-*/
-class TokenMultiply extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\*';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 180;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("Multiplication requires two operators");
- }
-
- $result = $op1->getValue() * $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenNotEqual.php b/src/NXP/Classes/Token/TokenNotEqual.php
deleted file mode 100644
index 21c9454..0000000
--- a/src/NXP/Classes/Token/TokenNotEqual.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenNotEqual extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\!\=';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 140;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("!= requires two operators");
- }
-
- $result = $op1->getValue() != $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenNumber.php b/src/NXP/Classes/Token/TokenNumber.php
deleted file mode 100644
index 982e316..0000000
--- a/src/NXP/Classes/Token/TokenNumber.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenNumber extends AbstractContainerToken
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\-?\d+\.?\d*(E-?\d+)?';
- }
-}
diff --git a/src/NXP/Classes/Token/TokenOr.php b/src/NXP/Classes/Token/TokenOr.php
deleted file mode 100644
index 86a4e53..0000000
--- a/src/NXP/Classes/Token/TokenOr.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace NXP\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-class TokenOr extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\|\|';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 90;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("|| requires two operators");
- }
-
- $result = $op1->getValue() || $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenPlus.php b/src/NXP/Classes/Token/TokenPlus.php
deleted file mode 100644
index 494f786..0000000
--- a/src/NXP/Classes/Token/TokenPlus.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?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\Classes\Token;
-
-use NXP\Exception\IncorrectExpressionException;
-
-/**
-* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
-*/
-class TokenPlus extends AbstractOperator
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\+';
- }
-
- /**
- * @return int
- */
- public function getPriority()
- {
- return 170;
- }
-
- /**
- * @return string
- */
- public function getAssociation()
- {
- return self::LEFT_ASSOC;
- }
-
- /**
- * @param InterfaceToken[] $stack
- *
- * @return $this
- *
- * @throws \NXP\Exception\IncorrectExpressionException
- */
- public function execute(&$stack)
- {
- $op2 = array_pop($stack);
- $op1 = array_pop($stack);
-
- if ($op1 === null || $op2 === null) {
- throw new IncorrectExpressionException("Addition requires two operators");
- }
-
- $result = $op1->getValue() + $op2->getValue();
-
- return new TokenNumber($result);
- }
-}
diff --git a/src/NXP/Classes/Token/TokenRightBracket.php b/src/NXP/Classes/Token/TokenRightBracket.php
deleted file mode 100644
index 306ae0b..0000000
--- a/src/NXP/Classes/Token/TokenRightBracket.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenRightBracket implements InterfaceToken
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\)';
- }
-}
diff --git a/src/NXP/Classes/Token/TokenStringDoubleQuoted.php b/src/NXP/Classes/Token/TokenStringDoubleQuoted.php
deleted file mode 100644
index 6cff262..0000000
--- a/src/NXP/Classes/Token/TokenStringDoubleQuoted.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Bruce Wells <brucekwells@gmail.com>
- */
-class TokenStringDoubleQuoted extends AbstractContainerToken
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '"([^"]|"")*"';
- }
-}
diff --git a/src/NXP/Classes/Token/TokenStringSingleQuoted.php b/src/NXP/Classes/Token/TokenStringSingleQuoted.php
deleted file mode 100644
index 7a7ab92..0000000
--- a/src/NXP/Classes/Token/TokenStringSingleQuoted.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Bruce Wells <brucekwells@gmail.com>
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenStringSingleQuoted extends AbstractContainerToken
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return "'([^']|'')*'";
- }
-}
diff --git a/src/NXP/Classes/Token/TokenVariable.php b/src/NXP/Classes/Token/TokenVariable.php
deleted file mode 100644
index a4a820a..0000000
--- a/src/NXP/Classes/Token/TokenVariable.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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\Classes\Token;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenVariable extends AbstractContainerToken
-{
- /**
- * @return string
- */
- public static function getRegex()
- {
- return '\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
- }
-}
diff --git a/src/NXP/Classes/TokenFactory.php b/src/NXP/Classes/TokenFactory.php
deleted file mode 100644
index 70d1ba9..0000000
--- a/src/NXP/Classes/TokenFactory.php
+++ /dev/null
@@ -1,217 +0,0 @@
-<?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\Classes;
-
-use NXP\Classes\Token\InterfaceToken;
-use NXP\Classes\Token\TokenComma;
-use NXP\Classes\Token\TokenFunction;
-use NXP\Classes\Token\TokenLeftBracket;
-use NXP\Classes\Token\TokenNumber;
-use NXP\Classes\Token\TokenRightBracket;
-use NXP\Classes\Token\TokenStringSingleQuoted;
-use NXP\Classes\Token\TokenVariable;
-use NXP\Classes\Token\TokenStringDoubleQuoted;
-use NXP\Exception\UnknownFunctionException;
-use NXP\Exception\UnknownOperatorException;
-use NXP\Exception\UnknownTokenException;
-
-/**
- * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
- */
-class TokenFactory
-{
- /**
- * Available operators
- *
- * @var array
- */
- protected $operators = [];
-
- /**
- * Divide by zero reporting
- *
- * @var bool
- */
- protected $divideByZeroReporting = false;
-
- /**
- * Available functions
- *
- * @var array
- */
- protected $functions = [];
-
- /**
- * Add function
- * @param string $name
- * @param callable $function
- * @param int $places
- * @return TokenFactory
- * @throws \ReflectionException
- */
- public function addFunction($name, callable $function, $places = null)
- {
- if ($places === null) {
- $reflector = new \ReflectionFunction($function);
- $places = $reflector->getNumberOfParameters();
- }
- $this->functions[$name] = [$places, $function];
-
- return $this;
- }
-
- /**
- * get functions
- *
- * @return array containing callback and places indexed by
- * function name
- */
- public function getFunctions()
- {
- return $this->functions;
- }
-
- /**
- * Add operator
- * @param string $operatorClass
- * @return TokenFactory
- * @throws UnknownOperatorException
- * @throws \ReflectionException
- */
- public function addOperator($operatorClass)
- {
- $class = new \ReflectionClass($operatorClass);
-
- if (!in_array('NXP\Classes\Token\InterfaceToken', $class->getInterfaceNames())) {
- throw new UnknownOperatorException($operatorClass);
- }
-
- $this->operators[$operatorClass::getRegex()] = $operatorClass;
-
- return $this;
- }
-
- /**
- * Get registered operators
- *
- * @return array of operator class names
- */
- public function getOperators()
- {
- return $this->operators;
- }
-
- /**
- * 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()
- {
- $operatorsRegex = '';
- foreach ($this->operators as $operator) {
- $operatorsRegex .= '|(' . $operator::getRegex() . ')';
- }
- $s = sprintf(
- '/(%s)|(%s)|(%s)|(%s)|(%s)|([%s%s%s])',
- TokenNumber::getRegex(),
- TokenStringDoubleQuoted::getRegex(),
- TokenStringSingleQuoted::getRegex(),
- TokenFunction::getRegex(),
- TokenVariable::getRegex(),
- TokenLeftBracket::getRegex(),
- TokenRightBracket::getRegex(),
- TokenComma::getRegex()
- );
- $s .= $operatorsRegex . '/i';
-
- return $s;
- }
-
- /**
- * @param string $token
- * @return InterfaceToken
- * @throws UnknownTokenException
- * @throws UnknownFunctionException
- */
- public function createToken($token)
- {
- if (is_numeric($token)) {
- return new TokenNumber($token);
- }
-
- if ($token == '(') {
- return new TokenLeftBracket();
- }
-
- if ($token == ')') {
- return new TokenRightBracket();
- }
-
- if ($token[0] == '"') {
- return new TokenStringDoubleQuoted(str_replace('"', '', $token));
- }
-
- if ($token[0] == "'") {
- return new TokenStringSingleQuoted(str_replace("'", '', $token));
- }
-
- if ($token == ',') {
- return new TokenComma();
- }
-
- foreach ($this->operators as $operator) {
- $regex = sprintf('/%s/i', $operator::getRegex());
- if (preg_match($regex, $token)) {
- $op = new $operator;
- return $op->setDivisionByZeroException($this->getDivisionByZeroException());
- }
- }
-
- $regex = sprintf('/%s/i', TokenVariable::getRegex());
- if (preg_match($regex, $token)) {
- return new TokenVariable(substr($token, 1));
- }
-
- $regex = sprintf('/%s/i', TokenFunction::getRegex());
- if (preg_match($regex, $token)) {
- if (isset($this->functions[$token])) {
- return new TokenFunction($this->functions[$token]);
- } else {
- throw new UnknownFunctionException($token);
- }
- }
-
- throw new UnknownTokenException($token);
- }
-}
diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php
new file mode 100644
index 0000000..b72b869
--- /dev/null
+++ b/src/NXP/Classes/Tokenizer.php
@@ -0,0 +1,302 @@
+<?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\Classes;
+
+use NXP\Exception\IncorrectBracketsException;
+use NXP\Exception\UnknownOperatorException;
+use RuntimeException;
+use SplStack;
+
+/**
+ * @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
+ */
+class Tokenizer
+{
+ /**
+ * @var Token[]
+ */
+ public $tokens = [];
+ /**
+ * @var string
+ */
+ private $input = "";
+ /**
+ * @var string
+ */
+ private $numberBuffer = "";
+ /**
+ * @var string
+ */
+ private $stringBuffer = "";
+ /**
+ * @var bool
+ */
+ private $allowNegative = true;
+ /**
+ * @var Operator[]
+ */
+ private $operators = [];
+
+ /**
+ * @var bool
+ */
+ private $inSingleQuotedString = false;
+
+ /**
+ * @var bool
+ */
+ private $inDoubleQuotedString = false;
+
+ /**
+ * Tokenizer constructor.
+ * @param string $input
+ * @param Operator[] $operators
+ */
+ public function __construct(string $input, array $operators)
+ {
+ $this->input = $input;
+ $this->operators = $operators;
+ }
+
+ public function tokenize()
+ {
+ foreach (str_split($this->input, 1) as $ch) {
+ switch (true) {
+ case $this->inSingleQuotedString:
+ if ($ch === "'") {
+ $this->tokens[] = new Token(Token::String, $this->stringBuffer);
+ $this->inSingleQuotedString = false;
+ $this->stringBuffer = "";
+ continue 2;
+ }
+ $this->stringBuffer .= $ch;
+ continue 2;
+ case $this->inDoubleQuotedString:
+ if ($ch === "\"") {
+ $this->tokens[] = new Token(Token::String, $this->stringBuffer);
+ $this->inDoubleQuotedString = false;
+ $this->stringBuffer = "";
+ continue 2;
+ }
+ $this->stringBuffer .= $ch;
+ continue 2;
+ case $ch == " " || $ch == "\n" || $ch == "\r" || $ch == "\t":
+ $this->tokens[] = new Token(Token::Space, "");
+ continue 2;
+ case $this->isNumber($ch):
+ if ($this->stringBuffer != "") {
+ $this->stringBuffer .= $ch;
+ continue 2;
+ }
+ $this->numberBuffer .= $ch;
+ $this->allowNegative = false;
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case strtolower($ch) === "e":
+ if ($this->numberBuffer != "" && strpos($this->numberBuffer, ".") !== false) {
+ $this->numberBuffer .= "e";
+ $this->allowNegative = true;
+ break;
+ }
+ case $this->isAlpha($ch):
+ if ($this->numberBuffer != "") {
+ $this->emptyNumberBufferAsLiteral();
+ $this->tokens[] = new Token(Token::Operator, "*");
+ }
+ $this->allowNegative = false;
+ $this->stringBuffer .= $ch;
+ break;
+ case $ch == "\"":
+ $this->inDoubleQuotedString = true;
+ continue 2;
+ case $ch == "'":
+ $this->inSingleQuotedString = true;
+ continue 2;
+
+ case $this->isDot($ch):
+ $this->numberBuffer .= $ch;
+ $this->allowNegative = false;
+ break;
+ case $this->isLP($ch):
+ if ($this->stringBuffer != "") {
+ $this->tokens[] = new Token(Token::Function, $this->stringBuffer);
+ $this->stringBuffer = "";
+ } elseif ($this->numberBuffer != "") {
+ $this->emptyNumberBufferAsLiteral();
+ $this->tokens[] = new Token(Token::Operator, "*");
+ }
+ $this->allowNegative = true;
+ $this->tokens[] = new Token(Token::LeftParenthesis, "");
+ break;
+ case $this->isRP($ch):
+ $this->emptyNumberBufferAsLiteral();
+ $this->emptyStrBufferAsVariable();
+ $this->allowNegative = false;
+ $this->tokens[] = new Token(Token::RightParenthesis, "");
+ break;
+ case $this->isComma($ch):
+ $this->emptyNumberBufferAsLiteral();
+ $this->emptyStrBufferAsVariable();
+ $this->allowNegative = true;
+ $this->tokens[] = new Token(Token::ParamSeparator, "");
+ break;
+ default:
+ if ($this->allowNegative && $ch == "-") {
+ $this->allowNegative = false;
+ $this->numberBuffer .= "-";
+ continue 2;
+ }
+ $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;
+ } else {
+ $this->tokens[] = new Token(Token::Operator, $ch);
+ }
+ } else {
+ $this->tokens[] = new Token(Token::Operator, $ch);
+ }
+ $this->allowNegative = true;
+ }
+ }
+ $this->emptyNumberBufferAsLiteral();
+ $this->emptyStrBufferAsVariable();
+ return $this;
+ }
+
+ private function isNumber($ch)
+ {
+ return $ch >= '0' && $ch <= '9';
+ }
+
+ private function isAlpha($ch)
+ {
+ return $ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || $ch == '_';
+ }
+
+ private function emptyNumberBufferAsLiteral()
+ {
+ if ($this->numberBuffer != "") {
+ $this->tokens[] = new Token(Token::Literal, $this->numberBuffer);
+ $this->numberBuffer = "";
+ }
+ }
+
+ private function isDot($ch)
+ {
+ return $ch == '.';
+ }
+
+ private function isLP($ch)
+ {
+ return $ch == '(';
+ }
+
+ private function isRP($ch)
+ {
+ return $ch == ')';
+ }
+
+ private function emptyStrBufferAsVariable()
+ {
+ if ($this->stringBuffer != "") {
+ $this->tokens[] = new Token(Token::Variable, $this->stringBuffer);
+ $this->stringBuffer = "";
+ }
+ }
+
+ private function isComma($ch)
+ {
+ return $ch == ',';
+ }
+
+ /**
+ * @return Token[] Array of tokens in revers polish notation
+ * @throws IncorrectBracketsException
+ * @throws UnknownOperatorException
+ */
+ public function buildReversePolishNotation()
+ {
+ $tokens = [];
+ /** @var SplStack<Token> $stack */
+ $stack = new SplStack();
+ foreach ($this->tokens as $token) {
+ switch ($token->type) {
+ case Token::Literal:
+ case Token::Variable:
+ case Token::String:
+ $tokens[] = $token;
+ break;
+ case Token::Function:
+ case Token::LeftParenthesis:
+ $stack->push($token);
+ break;
+ case Token::ParamSeparator:
+ while ($stack->top()->type !== Token::LeftParenthesis) {
+ if ($stack->count() === 0) {
+ throw new IncorrectBracketsException();
+ }
+ $tokens[] = $stack->pop();
+ }
+ break;
+ case Token::Operator:
+ if (!array_key_exists($token->value, $this->operators)) {
+ throw new UnknownOperatorException();
+ }
+ $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();
+ }
+ $op2 = $this->operators[$stack->top()->value];
+ if ($op2->priority >= $op1->priority) {
+ $tokens[] = $stack->pop();
+ continue;
+ }
+ break;
+ }
+ $stack->push($token);
+ break;
+ case Token::RightParenthesis:
+ while (true) {
+ try {
+ $ctoken = $stack->pop();
+ if ($ctoken->type === Token::LeftParenthesis) {
+ break;
+ }
+ $tokens[] = $ctoken;
+ } catch (RuntimeException $e) {
+ throw new IncorrectBracketsException();
+ }
+ }
+ if ($stack->count() > 0 && $stack->top()->type == Token::Function) {
+ $tokens[] = $stack->pop();
+ }
+ break;
+ case Token::Space:
+ //do nothing
+ }
+ }
+ while ($stack->count() !== 0) {
+ if ($stack->top()->type === Token::LeftParenthesis || $stack->top()->type === Token::RightParenthesis) {
+ throw new IncorrectBracketsException();
+ }
+ if ($stack->top()->type === Token::Space) {
+ $stack->pop();
+ continue;
+ }
+ $tokens[] = $stack->pop();
+ }
+ return $tokens;
+ }
+}
+
diff --git a/src/NXP/Exception/UnknownTokenException.php b/src/NXP/Exception/UnknownTokenException.php
deleted file mode 100644
index bba28a4..0000000
--- a/src/NXP/Exception/UnknownTokenException.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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 UnknownTokenException extends MathExecutorException
-{
-}
diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php
index e9ce0ed..a0a3f9b 100644
--- a/src/NXP/MathExecutor.php
+++ b/src/NXP/MathExecutor.php
@@ -12,9 +12,11 @@
namespace NXP;
use NXP\Classes\Calculator;
-use NXP\Classes\Lexer;
-use NXP\Classes\TokenFactory;
-use NXP\Exception\UnknownVariableException;
+use NXP\Classes\CustomFunction;
+use NXP\Classes\Operator;
+use NXP\Classes\Tokenizer;
+use NXP\Exception\DivisionByZeroException;
+use ReflectionException;
/**
* Class MathExecutor
@@ -27,12 +29,17 @@ class MathExecutor
*
* @var array
*/
- private $variables = [];
+ public $variables = [];
/**
- * @var TokenFactory
+ * @var Operator[]
*/
- private $tokenFactory;
+ public $operators = [];
+
+ /**
+ * @var CustomFunction[]
+ */
+ public $functions = [];
/**
* @var array
@@ -47,240 +54,140 @@ class MathExecutor
$this->addDefaults();
}
- public function __clone()
- {
- $this->addDefaults();
- }
-
- /**
- * Get all vars
- *
- * @return array
- */
- public function getVars()
- {
- return $this->variables;
- }
-
/**
- * Get a specific var
- *
- * @param string $variable
- * @return integer|float
- * @throws UnknownVariableException
- */
- public function getVar($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 \Exception
- */
- public function setVar($variable, $value)
- {
- if (!is_numeric($value)) {
- throw new \Exception("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
+ * Set default operands and functions
+ * @throws ReflectionException
*/
- public function setVars(array $variables, $clear = true)
+ protected function addDefaults()
{
- if ($clear) {
- $this->removeVars();
+ foreach ($this->defaultOperators() as $name => $operator) {
+ list($callable, $priority, $isRightAssoc) = $operator;
+ $this->addOperator(new Operator($name, $isRightAssoc, $priority, $callable));
}
-
- foreach ($variables as $name => $value) {
- $this->setVar($name, $value);
+ foreach ($this->defaultFunctions() as $name => $callable) {
+ $this->addFunction($name, $callable);
}
-
- return $this;
+ $this->variables = $this->defaultVars();
}
/**
- * Remove variable from executor
+ * Get the default operators
*
- * @param string $variable
- * @return MathExecutor
- */
- public function removeVar($variable)
- {
- unset ($this->variables[$variable]);
-
- return $this;
- }
-
- /**
- * Remove all variables
- * @return MathExecutor
+ * @return array of class names
*/
- public function removeVars()
+ protected function defaultOperators()
{
- $this->variables = [];
-
- return $this;
+ return [
+ '+' => [
+ function ($a, $b) {
+ return $a + $b;
+ },
+ 170,
+ false
+ ],
+ '-' => [
+ function ($a, $b) {
+ return $a - $b;
+ },
+ 170,
+ false
+ ],
+ '*' => [
+ function ($a, $b) {
+ return $a * $b;
+ },
+ 180,
+ false
+ ],
+ '/' => [
+ function ($a, $b) {
+ if ($b == 0) {
+ throw new DivisionByZeroException();
+ }
+ return $a / $b;
+ },
+ 180,
+ false
+ ],
+ '^' => [
+ function ($a, $b) {
+ return pow($a, $b);
+ },
+ 220,
+ true
+ ],
+ '&&' => [
+ function ($a, $b) {
+ return $a && $b;
+ },
+ 100,
+ false
+ ],
+ '||' => [
+ function ($a, $b) {
+ return $a || $b;
+ },
+ 90,
+ false
+ ],
+ '==' => [
+ function ($a, $b) {
+ return $a == $b;
+ },
+ 140,
+ false
+ ],
+ '!=' => [
+ function ($a, $b) {
+ return $a != $b;
+ },
+ 140,
+ false
+ ],
+ '>=' => [
+ function ($a, $b) {
+ return $a >= $b;
+ },
+ 150,
+ false
+ ],
+ '>' => [
+ function ($a, $b) {
+ return $a > $b;
+ },
+ 150,
+ false
+ ],
+ '<=' => [
+ function ($a, $b) {
+ return $a <= $b;
+ },
+ 150,
+ false
+ ],
+ '<' => [
+ function ($a, $b) {
+ return $a < $b;
+ },
+ 150,
+ false
+ ],
+ ];
}
/**
* Add operator to executor
*
- * @param string $operatorClass Class of operator token
- * @return MathExecutor
- * @throws Exception\UnknownOperatorException
- */
- public function addOperator($operatorClass)
- {
- $this->tokenFactory->addOperator($operatorClass);
-
- return $this;
- }
-
- /**
- * Get all registered operators to executor
- *
- * @return array of operator class names
- */
- public function getOperators()
- {
- return $this->tokenFactory->getOperators();
- }
-
- /**
- * Add function to executor
- *
- * @param string $name Name of function
- * @param callable $function Function
- * @param int $places Count of arguments
- * @return MathExecutor
- * @throws \ReflectionException
- */
- public function addFunction($name, $function = null, $places = null)
- {
- $this->tokenFactory->addFunction($name, $function, $places);
-
- return $this;
- }
-
- /**
- * Get all registered functions
- *
- * @return array containing callback and places indexed by
- * function name
- */
- public function getFunctions()
- {
- return $this->tokenFactory->getFunctions();
- }
-
- /**
- * Set division by zero exception reporting
- *
- * @param bool $exception default true
+ * @param Operator $operator
* @return MathExecutor
*/
- public function setDivisionByZeroException($exception = true)
+ public function addOperator(Operator $operator)
{
- $this->tokenFactory->setDivisionByZeroException($exception);
+ $this->operators[$operator->operator] = $operator;
return $this;
}
/**
- * Get division by zero exception status
- *
- * @return bool
- */
- public function getDivisionByZeroException()
- {
- return $this->tokenFactory->getDivisionByZeroException();
- }
-
- /**
- * Execute expression
- *
- * @param $expression
- * @return number
- */
- public function execute($expression)
- {
- $cachekey = (string)$expression;
- if (!array_key_exists($cachekey, $this->cache)) {
- $lexer = new Lexer($this->tokenFactory);
- $tokensStream = $lexer->stringToTokensStream($expression);
- $tokens = $lexer->buildReversePolishNotation($tokensStream);
- $this->cache[$cachekey] = $tokens;
- } else {
- $tokens = $this->cache[$cachekey];
- }
- $calculator = new Calculator();
- $result = $calculator->calculate($tokens, $this->variables);
-
- return $result;
- }
-
- /**
- * Set default operands and functions
- */
- protected function addDefaults()
- {
- $this->tokenFactory = new TokenFactory();
-
- foreach ($this->defaultOperators() as $operatorClass) {
- $this->tokenFactory->addOperator($operatorClass);
- }
-
- foreach ($this->defaultFunctions() as $name => $callable) {
- $this->tokenFactory->addFunction($name, $callable);
- }
-
- $this->setVars($this->defaultVars());
- }
-
- /**
- * Get the default operators
- *
- * @return array of class names
- */
- protected function defaultOperators()
- {
- return [
- \NXP\Classes\Token\TokenPlus::class,
- \NXP\Classes\Token\TokenMinus::class,
- \NXP\Classes\Token\TokenMultiply::class,
- \NXP\Classes\Token\TokenDivision::class,
- \NXP\Classes\Token\TokenDegree::class,
- \NXP\Classes\Token\TokenAnd::class,
- \NXP\Classes\Token\TokenOr::class,
- \NXP\Classes\Token\TokenEqual::class,
- \NXP\Classes\Token\TokenNotEqual::class,
- \NXP\Classes\Token\TokenGreaterThanOrEqual::class,
- \NXP\Classes\Token\TokenGreaterThan::class,
- \NXP\Classes\Token\TokenLessThanOrEqual::class,
- \NXP\Classes\Token\TokenLessThan::class,
- ];
- }
-
- /**
* Gets the default functions as an array. Key is function name
* and value is the function as a closure.
*
@@ -425,6 +332,44 @@ class MathExecutor
}
/**
+ * Execute expression
+ *
+ * @param $expression
+ * @return number
+ * @throws Exception\IncorrectExpressionException
+ * @throws Exception\IncorrectBracketsException
+ * @throws Exception\UnknownOperatorException
+ * @throws Exception\UnknownVariableException
+ */
+ public function execute($expression)
+ {
+ $cachekey = (string)$expression;
+ if (!array_key_exists($cachekey, $this->cache)) {
+ $tokens = (new Tokenizer($expression, $this->operators))->tokenize()->buildReversePolishNotation();
+ $this->cache[$cachekey] = $tokens;
+ } else {
+ $tokens = $this->cache[$cachekey];
+ }
+ $calculator = new Calculator($this->functions, $this->operators);
+ return $calculator->calculate($tokens, $this->variables);
+ }
+
+ /**
+ * Add function to executor
+ *
+ * @param string $name Name of function
+ * @param callable $function Function
+ * @param int $places Count of arguments
+ * @return MathExecutor
+ * @throws ReflectionException
+ */
+ public function addFunction($name, $function = null, $places = null)
+ {
+ $this->functions[$name] = new CustomFunction($name, $function, $places);
+ return $this;
+ }
+
+ /**
* Returns the default variables names as key/value pairs
*
* @return array
@@ -433,7 +378,12 @@ class MathExecutor
{
return [
'pi' => 3.14159265359,
- 'e' => 2.71828182846
+ 'e' => 2.71828182846
];
}
+
+ public function __clone()
+ {
+ $this->addDefaults();
+ }
}
diff --git a/tests/MathTest.php b/tests/MathTest.php
index 7cbc7de..958f14c 100644
--- a/tests/MathTest.php
+++ b/tests/MathTest.php
@@ -11,17 +11,15 @@
namespace NXP\Tests;
-use NXP\MathExecutor;
+use Exception;
+use NXP\Classes\Operator;
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;
+use NXP\MathExecutor;
+use PHPUnit\Framework\TestCase;
-class MathTest extends \PHPUnit\Framework\TestCase
+class MathTest extends TestCase
{
/**
* @dataProvider providerExpressions
@@ -32,15 +30,20 @@ class MathTest extends \PHPUnit\Framework\TestCase
/** @var float $phpResult */
eval('$phpResult = ' . $expression . ';');
- $this->assertEquals($phpResult, $calculator->execute($expression), "Expression was: ${expression}");
+ try {
+ $result = $calculator->execute($expression);
+ } catch (Exception $e) {
+ $this->fail(sprintf("Exception: %s (%s:%d), expression was: %s", get_class($e), $e->getFile(), $e->getLine(), $expression));
+ }
+ $this->assertEquals($phpResult, $result, "Expression was: ${expression}");
}
/**
* Expressions data provider
- *
- * Most tests can go in here. The idea is that each expression will be evaluated by MathExecutor and by PHP with eval.
- * The results should be the same. If they are not, then the test fails. No need to add extra test unless you are doing
- * something more complete and not a simple mathmatical expression.
+ *
+ * Most tests can go in here. The idea is that each expression will be evaluated by MathExecutor and by PHP with eval.
+ * The results should be the same. If they are not, then the test fails. No need to add extra test unless you are doing
+ * something more complete and not a simple mathmatical expression.
*/
public function providerExpressions()
{
@@ -223,13 +226,18 @@ class MathTest extends \PHPUnit\Framework\TestCase
public function testZeroDivision()
{
$calculator = new MathExecutor();
+ $calculator->addOperator(new Operator("/", false, 180, function ($a, $b) {
+ if ($b == 0) {
+ return 0;
+ }
+ return $a / $b;
+ }));
$this->assertEquals(0, $calculator->execute('10 / 0'));
}
public function testZeroDivisionException()
{
$calculator = new MathExecutor();
- $calculator->setDivisionByZeroException();
$this->expectException(DivisionByZeroException::class);
$calculator->execute('10 / 0');
}
@@ -244,7 +252,9 @@ class MathTest extends \PHPUnit\Framework\TestCase
{
$calculator = new MathExecutor();
- $calculator->addFunction('concat', function ($arg1, $arg2) {return $arg1.$arg2;});
+ $calculator->addFunction('concat', function ($arg1, $arg2) {
+ return $arg1 . $arg2;
+ });
$this->assertEquals('testing', $calculator->execute('concat("test","ing")'));
$this->assertEquals('testing', $calculator->execute("concat('test','ing')"));
}
@@ -252,8 +262,10 @@ class MathTest extends \PHPUnit\Framework\TestCase
public function testFunction()
{
$calculator = new MathExecutor();
- $calculator->addFunction('round', function ($arg) {return round($arg);});
- $this->assertEquals(round(100/30), $calculator->execute('round(100/30)'));
+ $calculator->addFunction('round', function ($arg) {
+ return round($arg);
+ });
+ $this->assertEquals(round(100 / 30), $calculator->execute('round(100/30)'));
}
public function testFunctionIf()
@@ -280,12 +292,12 @@ class MathTest extends \PHPUnit\Framework\TestCase
public function testEvaluateFunctionParameters()
{
$calculator = new MathExecutor();
- $calculator->addFunction('round', function ($value, $decimals)
- {
+ $calculator->addFunction('round', function ($value, $decimals) {
return round($value, $decimals);
- }
+ }
);
$expression = 'round(100 * 1.111111, 2)';
+ $phpResult = 0;
eval('$phpResult = ' . $expression . ';');
$this->assertEquals($phpResult, $calculator->execute($expression));
$expression = 'round((100*0.04)+(((100*1.02)+0.5)*1.28),2)';
@@ -296,7 +308,9 @@ class MathTest extends \PHPUnit\Framework\TestCase
public function testFunctionsWithQuotes()
{
$calculator = new MathExecutor();
- $calculator->addFunction('concat', function($first, $second){return $first.$second;});
+ $calculator->addFunction('concat', function ($first, $second) {
+ return $first . $second;
+ });
$this->assertEquals('testing', $calculator->execute('concat("test", "ing")'));
$this->assertEquals('testing', $calculator->execute("concat('test', 'ing')"));
}
@@ -305,11 +319,10 @@ class MathTest extends \PHPUnit\Framework\TestCase
{
$calculator = new MathExecutor();
$testString = "some, long. arg; with: different-separators!";
- $calculator->addFunction('test', function ($arg) use ($testString)
- {
+ $calculator->addFunction('test', function ($arg) use ($testString) {
$this->assertEquals($testString, $arg);
return 0;
- }
+ }
);
$calculator->execute('test("' . $testString . '")'); // single quotes
$calculator->execute("test('" . $testString . "')"); // double quotes