From 17cc5b9dc48b19e0dee1937080601a9fc2890af4 Mon Sep 17 00:00:00 2001 From: Bruce Wells Date: Tue, 16 Feb 2021 22:13:44 -0500 Subject: [PATCH] Unary Minus (#84) Fixed unary minus to allow a minus sign in front of functions and parentheses. --- src/NXP/Classes/Tokenizer.php | 70 +++++++++++++++++++---------------- src/NXP/MathExecutor.php | 7 ++++ tests/MathTest.php | 20 ++++++++++ 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php index 203724a..bb6fa15 100644 --- a/src/NXP/Classes/Tokenizer.php +++ b/src/NXP/Classes/Tokenizer.php @@ -27,15 +27,15 @@ class Tokenizer /** * @var string */ - private $input = ""; + private $input = ''; /** * @var string */ - private $numberBuffer = ""; + private $numberBuffer = ''; /** * @var string */ - private $stringBuffer = ""; + private $stringBuffer = ''; /** * @var bool */ @@ -74,25 +74,25 @@ public function tokenize() : self if ($ch === "'") { $this->tokens[] = new Token(Token::String, $this->stringBuffer); $this->inSingleQuotedString = false; - $this->stringBuffer = ""; + $this->stringBuffer = ''; continue 2; } $this->stringBuffer .= $ch; continue 2; case $this->inDoubleQuotedString: - if ($ch === "\"") { + if ($ch === '"') { $this->tokens[] = new Token(Token::String, $this->stringBuffer); $this->inDoubleQuotedString = false; - $this->stringBuffer = ""; + $this->stringBuffer = ''; continue 2; } $this->stringBuffer .= $ch; continue 2; - case $ch == " " || $ch == "\n" || $ch == "\r" || $ch == "\t": - $this->tokens[] = new Token(Token::Space, ""); + case $ch == ' ' || $ch == "\n" || $ch == "\r" || $ch == "\t": + $this->tokens[] = new Token(Token::Space, ''); continue 2; case $this->isNumber($ch): - if ($this->stringBuffer != "") { + if ($this->stringBuffer != '') { $this->stringBuffer .= $ch; continue 2; } @@ -100,22 +100,22 @@ public function tokenize() : self $this->allowNegative = false; break; /** @noinspection PhpMissingBreakStatementInspection */ - case strtolower($ch) === "e": - if ($this->numberBuffer != "" && strpos($this->numberBuffer, ".") !== false) { - $this->numberBuffer .= "e"; - $this->allowNegative = true; + case strtolower($ch) === 'e': + if (strlen($this->numberBuffer) && strpos($this->numberBuffer, '.') !== false) { + $this->numberBuffer .= 'e'; + $this->allowNegative = false; break; } // no break case $this->isAlpha($ch): - if ($this->numberBuffer != "") { + if (strlen($this->numberBuffer)) { $this->emptyNumberBufferAsLiteral(); - $this->tokens[] = new Token(Token::Operator, "*"); + $this->tokens[] = new Token(Token::Operator, '*'); } $this->allowNegative = false; $this->stringBuffer .= $ch; break; - case $ch == "\"": + case $ch == '"': $this->inDoubleQuotedString = true; continue 2; case $ch == "'": @@ -127,33 +127,41 @@ public function tokenize() : self $this->allowNegative = false; break; case $this->isLP($ch): - if ($this->stringBuffer != "") { + if ($this->stringBuffer != '') { $this->tokens[] = new Token(Token::Function, $this->stringBuffer); - $this->stringBuffer = ""; - } elseif ($this->numberBuffer != "") { + $this->stringBuffer = ''; + } elseif (strlen($this->numberBuffer)) { $this->emptyNumberBufferAsLiteral(); - $this->tokens[] = new Token(Token::Operator, "*"); + $this->tokens[] = new Token(Token::Operator, '*'); } $this->allowNegative = true; - $this->tokens[] = new Token(Token::LeftParenthesis, ""); + $this->tokens[] = new Token(Token::LeftParenthesis, ''); break; case $this->isRP($ch): $this->emptyNumberBufferAsLiteral(); $this->emptyStrBufferAsVariable(); $this->allowNegative = false; - $this->tokens[] = new Token(Token::RightParenthesis, ""); + $this->tokens[] = new Token(Token::RightParenthesis, ''); break; case $this->isComma($ch): $this->emptyNumberBufferAsLiteral(); $this->emptyStrBufferAsVariable(); $this->allowNegative = true; - $this->tokens[] = new Token(Token::ParamSeparator, ""); + $this->tokens[] = new Token(Token::ParamSeparator, ''); break; default: - if ($this->allowNegative && $ch == "-") { - $this->allowNegative = false; - $this->numberBuffer .= "-"; - continue 2; + // special case for unary minus + if ($ch == '-') { + if ($this->allowNegative) { + $this->allowNegative = false; + $this->tokens[] = new Token(Token::Operator, '`'); + continue 2; + } + // could be in exponent, in which case negative should be added to the numberBuffer + if ($this->numberBuffer && $this->numberBuffer[strlen($this->numberBuffer) - 1] == 'e') { + $this->numberBuffer .= '-'; + continue 2; + } } $this->emptyNumberBufferAsLiteral(); $this->emptyStrBufferAsVariable(); @@ -188,9 +196,9 @@ private function isAlpha(string $ch) : bool private function emptyNumberBufferAsLiteral() : void { - if ($this->numberBuffer != "") { + if (strlen($this->numberBuffer)) { $this->tokens[] = new Token(Token::Literal, $this->numberBuffer); - $this->numberBuffer = ""; + $this->numberBuffer = ''; } } @@ -211,9 +219,9 @@ private function isRP(string $ch) : bool private function emptyStrBufferAsVariable() : void { - if ($this->stringBuffer != "") { + if ($this->stringBuffer != '') { $this->tokens[] = new Token(Token::Variable, $this->stringBuffer); - $this->stringBuffer = ""; + $this->stringBuffer = ''; } } diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php index ca27539..b8e8a65 100644 --- a/src/NXP/MathExecutor.php +++ b/src/NXP/MathExecutor.php @@ -99,6 +99,13 @@ function ($a, $b) { 170, false ], + '`' => [ // unary minus token + function ($a) { + return 0 - $a; + }, + 200, + false + ], '*' => [ function ($a, $b) { return $a * $b; diff --git a/tests/MathTest.php b/tests/MathTest.php index 47d89b8..a843db5 100644 --- a/tests/MathTest.php +++ b/tests/MathTest.php @@ -147,6 +147,8 @@ public function providerExpressions() ['sin(10) * cos(50) / min(10, (20/2))'], ['sin(10) * cos(50) / min(10, (max(10,20)/2))'], + ['100500 * 3.5e5'], + ['100500 * 3.5e-5'], ['100500 * 3.5E5'], ['100500 * 3.5E-5'], @@ -208,6 +210,24 @@ public function providerExpressions() ['1 + (3 *-1)'], ['1 - 0'], ['1-0'], + + ['-(1.5)'], + ['-log(4)'], + ['0-acosh(1.5)'], + ['-acosh(1.5)'], + ['-(-4)'], + ['-(-4 + 5)'], + ['-(3 * 1)'], + ['-(-3 * -1)'], + ['-1 + (-3 * -1)'], + ['-1 + ( -3 * 1)'], + ['-1 + (3 *-1)'], + ['-1 - 0'], + ['-1-0'], + ['-(4*2)-5'], + ['-(4*-2)-5'], + ['-(-4*2) - 5'], + ['-4*-5'], ]; }