From 5bdc27efdb50434a6f44a01e158d5e13eff3a020 Mon Sep 17 00:00:00 2001 From: Bruce Wells Date: Mon, 26 Nov 2018 09:25:33 -0500 Subject: Fixed function in () block issue --- src/NXP/Classes/Lexer.php | 263 +++++++++++++++++++++++----------------------- 1 file changed, 133 insertions(+), 130 deletions(-) (limited to 'src/NXP') diff --git a/src/NXP/Classes/Lexer.php b/src/NXP/Classes/Lexer.php index 82b2c53..36eb43c 100644 --- a/src/NXP/Classes/Lexer.php +++ b/src/NXP/Classes/Lexer.php @@ -1,130 +1,133 @@ - - */ -class Lexer -{ - /** - * @var TokenFactory - */ - private $tokenFactory; - - public function __construct($tokenFactory) - { - $this->tokenFactory = $tokenFactory; - } - - /** - * @param string $input Source string of equation - * @return array Tokens stream - * @throws \NXP\Exception\IncorrectExpressionException - */ - 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 \NXP\Exception\IncorrectExpressionException - */ - public function buildReversePolishNotation($tokensStream) - { - $output = []; - $stack = []; - - foreach ($tokensStream as $token) { - if ($token instanceof TokenString) { - $output[] = $token; - } - if ($token instanceof TokenNumber) { - $output[] = $token; - } - if ($token instanceof TokenVariable) { - $output[] = $token; - } - if ($token instanceof TokenFunction) { - array_push($stack, $token); - } - if ($token instanceof TokenLeftBracket) { - array_push($stack, $token); - } - if ($token instanceof TokenComma) { - while (($current = array_pop($stack)) && (!$current instanceof TokenLeftBracket)) { - $output[] = $current; - if (empty($stack)) { - throw new IncorrectExpressionException(); - } - } - } - if ($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); - } - } - - if ($token instanceof AbstractOperator) { - while ( - count($stack) > 0 && - ($stack[count($stack)-1] instanceof InterfaceOperator) && - (( - $token->getAssociation() == AbstractOperator::LEFT_ASSOC && - $token->getPriority() <= $stack[count($stack)-1]->getPriority() - ) || ( - $token->getAssociation() == AbstractOperator::RIGHT_ASSOC && - $token->getPriority() < $stack[count($stack)-1]->getPriority() - )) - ) { - $output[] = array_pop($stack); - } - - array_push($stack, $token); - } - } - while (!empty($stack)) { - $token = array_pop($stack); - if ($token instanceof TokenLeftBracket || $token instanceof TokenRightBracket) { - throw new IncorrectBracketsException(); - } - $output[] = $token; - } - - return $output; - } -} + + */ +class Lexer +{ + /** + * @var TokenFactory + */ + private $tokenFactory; + + public function __construct($tokenFactory) + { + $this->tokenFactory = $tokenFactory; + } + + /** + * @param string $input Source string of equation + * @return array Tokens stream + * @throws \NXP\Exception\IncorrectExpressionException + */ + 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 \NXP\Exception\IncorrectExpressionException + */ + public function buildReversePolishNotation($tokensStream) + { + $output = []; + $stack = []; + + foreach ($tokensStream as $token) { + if ($token instanceof TokenString) { + $output[] = $token; + } + elseif ($token instanceof TokenNumber) { + $output[] = $token; + } + elseif ($token instanceof TokenVariable) { + $output[] = $token; + } + elseif ($token instanceof TokenFunction) { + array_push($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); + } + + array_push($stack, $token); + } + elseif ($token instanceof TokenLeftBracket) { + array_push($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); + } + } + } + while (!empty($stack)) { + $token = array_pop($stack); + if ($token instanceof TokenLeftBracket || $token instanceof TokenRightBracket) { + throw new IncorrectBracketsException(); + } + $output[] = $token; + } + + return $output; + } +} -- cgit v1.2.3 From 0729b6b9bc0e3cebb868f889e88676b95c121a50 Mon Sep 17 00:00:00 2001 From: Bruce Wells Date: Thu, 10 Jan 2019 12:45:54 -0500 Subject: Syncing from origin (#3) * Documentation fixes (#34) Fixing typos in and clarifying documentation. * MathExecutor allow override default operators, functions and vars (#36) --- src/NXP/Classes/TokenFactory.php | 11 +++-- src/NXP/MathExecutor.php | 99 ++++++++++++++++++++++++++++------------ 2 files changed, 78 insertions(+), 32 deletions(-) (limited to 'src/NXP') diff --git a/src/NXP/Classes/TokenFactory.php b/src/NXP/Classes/TokenFactory.php index 6956f31..4ed55af 100644 --- a/src/NXP/Classes/TokenFactory.php +++ b/src/NXP/Classes/TokenFactory.php @@ -50,12 +50,17 @@ class TokenFactory /** * Add function - * @param string $name + * @param string $name * @param callable $function - * @param int $places + * @param int $places + * @throws \ReflectionException */ - public function addFunction($name, callable $function, $places = 1) + public function addFunction($name, callable $function, $places = null) { + if ($places === null) { + $reflector = new \ReflectionFunction($function); + $places = $reflector->getNumberOfParameters(); + } $this->functions[$name] = [$places, $function]; } diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php index fbf6ab1..706f8af 100644 --- a/src/NXP/MathExecutor.php +++ b/src/NXP/MathExecutor.php @@ -53,35 +53,6 @@ class MathExecutor $this->addDefaults(); } - /** - * Set default operands and functions - */ - protected function addDefaults() - { - $this->tokenFactory = new TokenFactory(); - - $this->tokenFactory->addOperator('NXP\Classes\Token\TokenPlus'); - $this->tokenFactory->addOperator('NXP\Classes\Token\TokenMinus'); - $this->tokenFactory->addOperator('NXP\Classes\Token\TokenMultiply'); - $this->tokenFactory->addOperator('NXP\Classes\Token\TokenDivision'); - $this->tokenFactory->addOperator('NXP\Classes\Token\TokenDegree'); - - $this->tokenFactory->addFunction('sin', 'sin'); - $this->tokenFactory->addFunction('cos', 'cos'); - $this->tokenFactory->addFunction('tn', 'tan'); - $this->tokenFactory->addFunction('asin', 'asin'); - $this->tokenFactory->addFunction('acos', 'acos'); - $this->tokenFactory->addFunction('atn', 'atan'); - $this->tokenFactory->addFunction('min', 'min', 2); - $this->tokenFactory->addFunction('max', 'max', 2); - $this->tokenFactory->addFunction('avg', function($arg1, $arg2) { return ($arg1 + $arg2) / 2; }, 2); - - $this->setVars([ - 'pi' => 3.14159265359, - 'e' => 2.71828182846 - ]); - } - /** * Get all vars * @@ -262,4 +233,74 @@ class MathExecutor 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()); + } + + protected function defaultOperators() + { + return [ + 'NXP\Classes\Token\TokenPlus', + 'NXP\Classes\Token\TokenMinus', + 'NXP\Classes\Token\TokenMultiply', + 'NXP\Classes\Token\TokenDivision', + 'NXP\Classes\Token\TokenDegree', + ]; + } + + protected function defaultFunctions() + { + return [ + 'sin' => function ($arg) { + return sin($arg); + }, + 'cos' => function ($arg) { + return cos($arg); + }, + 'tn' => function ($arg) { + return tan($arg); + }, + 'asin' => function ($arg) { + return asin($arg); + }, + 'acos' => function ($arg) { + return acos($arg); + }, + 'atn' => function ($arg) { + return atan($arg); + }, + 'min' => function ($arg1, $arg2) { + return min($arg1, $arg2); + }, + 'max' => function ($arg1, $arg2) { + return max($arg1, $arg2); + }, + 'avg' => function ($arg1, $arg2) { + return ($arg1 + $arg2) / 2; + }, + ]; + } + + protected function defaultVars() + { + return [ + 'pi' => 3.14159265359, + 'e' => 2.71828182846 + ]; + } } -- cgit v1.2.3 From ba505031a329eaf632d1af646308482f9db57b68 Mon Sep 17 00:00:00 2001 From: Bruce Wells Date: Thu, 10 Jan 2019 13:47:27 -0500 Subject: Syncing to origin (#4) * Documentation fixes (#34) Fixing typos in and clarifying documentation. * MathExecutor allow override default operators, functions and vars (#36) * Added simple coc (#37) * Added simple coc * Fix * Replaceable operators (#38) * Updated from NeonXP/MathExecutor * Fixed function in () block issue * Fixing typos in and clarifying documentation. * Syncing from origin (#3) * Documentation fixes (#34) Fixing typos in and clarifying documentation. * MathExecutor allow override default operators, functions and vars (#36) * Allow for operators to be replaced based on regex expression --- README.md | 12 ++++++++++++ code-of-conduct.md | 9 +++++++++ src/NXP/Classes/TokenFactory.php | 3 +-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 code-of-conduct.md (limited to 'src/NXP') diff --git a/README.md b/README.md index 39490a4..6579a23 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A simple math expressions calculator * Exceptions on divide by zero, or treat as zero * Unary Minus (e.g. -3) * Pi ($pi) and Euler's number ($e) support to 11 decimal places +* Easily extendable ## Install via Composer: Stable branch @@ -152,3 +153,14 @@ Expressions can contain double or single quoted strings that are evaluated the s ```php echo $executor->execute("1 + '2.5' * '.5' + myFunction('category')"); ``` + +## Extending MathExecutor +You can add operators, functions and variables with the public methods in MathExecutor, but if you need to do more serious modifications to base behaviours, the easiest way to extend MathExecutor is to redefine the following methods in your derived class: +* defaultOperators +* defaultFunctions +* defaultVars + +This will allow you to remove functions and operators if needed, or implement different types more simply. + +Also note that you can replace an existing default operator by adding a new operator with the same regular expression string. For example if you just need to redefine TokenPlus, you can just add a new operator with the same regex string, in this case '\+'. + diff --git a/code-of-conduct.md b/code-of-conduct.md new file mode 100644 index 0000000..83e0f4e --- /dev/null +++ b/code-of-conduct.md @@ -0,0 +1,9 @@ +# Code of conduct + +We don't care who you are IRL. Be professional and responsible. + +If you are a good person, we are happy for you. + +If you are an asshole to us, we will be assholes in relation to you. + +> Do to no one what you yourself dislike \ No newline at end of file diff --git a/src/NXP/Classes/TokenFactory.php b/src/NXP/Classes/TokenFactory.php index 4ed55af..778cb59 100644 --- a/src/NXP/Classes/TokenFactory.php +++ b/src/NXP/Classes/TokenFactory.php @@ -88,8 +88,7 @@ class TokenFactory throw new UnknownOperatorException($operatorClass); } - $this->operators[] = $operatorClass; - $this->operators = array_unique($this->operators); + $this->operators[$operatorClass::getRegex()] = $operatorClass; } /** -- cgit v1.2.3