From 5d6b4a5dfdea6d4e2814391afcfcd9cf4259c046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20K=C4=B1zmaz?= Date: Tue, 17 May 2022 00:57:37 +0300 Subject: [PATCH] Full support for arrays => min, max and avg funcs accept array argument. Also array function is defined which return arguments as array. Square bracket arrays are also supported. (#108) valid expression -> "max([1,2,3])" valid expression -> "max(array(1,2,3))" valid expression -> "max($ages_arr)" valid expression -> "max(ages_arr())" --- README.md | 20 ++++++++++++++++---- src/NXP/Classes/Tokenizer.php | 11 +++++++++-- src/NXP/MathExecutor.php | 3 ++- tests/MathTest.php | 16 ++++++++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0c085c8..faeeb11 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Default functions: * arcctg (arccot, arccotan) * arcsec * arccsc (arccosec) +* array * asin (arcsin) * atan (atn, arctan, arctg) * atan2 @@ -84,7 +85,10 @@ Add custom function to executor: ```php $executor->addFunction('abs', function($arg) {return abs($arg);}); ``` -Function default parameters are not supported at this time. +Function default parameters (optional parameters) are also supported. +```php +$executor->addFunction('round', function($num, int $precision = 0) {return round($num, $precision);}); +``` ## Operators: Default operators: `+ - * / ^` @@ -139,12 +143,20 @@ $executor->setVar('var1', 0.15)->setVar('var2', 0.22); echo $executor->execute("$var1 + var2"); ``` -By default, variables must be scalar values (int, float, bool or string). If you would like to support another type, use **setVarValidationHandler** +Arrays are also supported (as variables, as func params or can be returned in user defined funcs): +```php +$executor->setVar('monthly_salaries', [1800, 1900, 1200, 1600]); + +echo $executor->execute("avg(monthly_salaries) * min([1.1, 1.3])"); +``` + + +By default, variables must be scalar values (int, float, bool or string) or array. If you would like to support another type, use **setVarValidationHandler** ```php $executor->setVarValidationHandler(function (string $name, $variable) { - // allow all scalars and null - if (is_scalar($variable) || $variable === null) { + // allow all scalars, array and null + if (is_scalar($variable) || is_array($variable) || $variable === null) { return; } // Allow variables of type DateTime, but not others diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php index 23e1cc9..c8b415d 100644 --- a/src/NXP/Classes/Tokenizer.php +++ b/src/NXP/Classes/Tokenizer.php @@ -76,6 +76,13 @@ class Tokenizer continue 2; + case '[' === $ch: + $this->tokens[] = new Token(Token::Function, 'array'); + $this->allowNegative = true; + $this->tokens[] = new Token(Token::LeftParenthesis, ''); + + continue 2; + case ' ' == $ch || "\n" == $ch || "\r" == $ch || "\t" == $ch: $this->tokens[] = new Token(Token::Space, ''); @@ -140,7 +147,7 @@ class Tokenizer break; - case $this->isRP($ch): + case $this->isRP($ch) || ']' === $ch : $this->emptyNumberBufferAsLiteral(); $this->emptyStrBufferAsVariable(); $this->allowNegative = false; @@ -196,8 +203,8 @@ class Tokenizer } /** - * @throws UnknownOperatorException * @throws IncorrectBracketsException + * @throws UnknownOperatorException * @return Token[] Array of tokens in revers polish notation */ public function buildReversePolishNotation() : array diff --git a/src/NXP/MathExecutor.php b/src/NXP/MathExecutor.php index e7e8259..dd90e22 100644 --- a/src/NXP/MathExecutor.php +++ b/src/NXP/MathExecutor.php @@ -469,7 +469,8 @@ class MathExecutor 'tan' => static fn($arg) => \tan($arg), 'tanh' => static fn($arg) => \tanh($arg), 'tn' => static fn($arg) => \tan($arg), - 'tg' => static fn($arg) => \tan($arg) + 'tg' => static fn($arg) => \tan($arg), + 'array' => static fn(...$args) => [...$args] ]; } diff --git a/tests/MathTest.php b/tests/MathTest.php index c39eec7..382d67d 100644 --- a/tests/MathTest.php +++ b/tests/MathTest.php @@ -305,6 +305,22 @@ class MathTest extends TestCase $this->assertEquals(100, $calculator->execute('10 ^ 2')); } + public function testArrays() : void + { + $calculator = new MathExecutor(); + $this->assertEquals([1, 5, 2], $calculator->execute('array(1, 5, 2)')); + $this->assertEquals([1, 5, 2], $calculator->execute('[1, 5, 2]')); + $this->assertEquals(\max([1, 5, 2]), $calculator->execute('max([1, 5, 2])')); + $this->assertEquals(\max([1, 5, 2]), $calculator->execute('max(array(1, 5, 2))')); + $calculator->addFunction('arr_with_max_elements', static function($arg1, ...$args) { + $args = \is_array($arg1) ? $arg1 : [$arg1, ...$args]; + \usort($args, static fn($arr1, $arr2) => \count($arr2) <=> \count($arr1)); + + return $args[0]; + }); + $this->assertEquals([3, 3, 3], $calculator->execute('arr_with_max_elements([[1],array(2,2),[3,3,3]])')); + } + public function testFunctionParameterOrder() : void { $calculator = new MathExecutor();