aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Wells <bruce.wells@simparel.com>2019-08-16 15:35:06 +0300
committerBruce Wells <bruce.wells@simparel.com>2019-08-16 15:35:06 +0300
commit2bc89df821e2b98577a1b8daa1fd7349b18dcf92 (patch)
tree50fad589e827aa2904c65b27a6f9cba759316458
parente03df64281a1c33639b13c9f490d4452c1a1784d (diff)
parente1b770d6c884d79b8c3c6226d2e6cc8cc5a633ca (diff)
Merge branch 'master' of https://github.com/phpfui/MathExecutor
# Conflicts: # src/NXP/Classes/Token/TokenFunction.php
-rw-r--r--README.md5
-rw-r--r--code-of-conduct.ru.md5
-rw-r--r--src/NXP/Classes/Calculator.php20
-rw-r--r--src/NXP/Classes/Lexer.php9
-rw-r--r--src/NXP/Classes/Token/TokenComma.php30
-rw-r--r--src/NXP/Classes/Token/TokenFunction.php3
-rw-r--r--tests/MathTest.php48
7 files changed, 86 insertions, 34 deletions
diff --git a/README.md b/README.md
index 0c87108..4863224 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# MathExecutor [![Stories in Ready](https://badge.waffle.io/NeonXP/MathExecutor.png?label=ready&title=Ready)](https://waffle.io/NeonXP/MathExecutor) [![Build Status](https://travis-ci.org/NeonXP/MathExecutor.png?branch=master)](https://travis-ci.org/NeonXP/MathExecutor)
-A simple math expressions calculator
+# A simple and extensible math expressions calculator
## Features:
* Built in support for +, -, *, / and power (^) operators plus ()
@@ -11,7 +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
+* Easily extensible
## Install via Composer:
```
@@ -43,6 +43,7 @@ Add custom function to executor:
```php
$executor->addFunction('abs', function($arg) {return abs($arg);});
```
+Function default parameters are not supported at this time.
Default parameters are not currently supported.
diff --git a/code-of-conduct.ru.md b/code-of-conduct.ru.md
new file mode 100644
index 0000000..04a566f
--- /dev/null
+++ b/code-of-conduct.ru.md
@@ -0,0 +1,5 @@
+# Кодекс поведения
+
+Нам всё равно, кто ты в реальной жизни, просто будь профессионалом и всё будет хорошо.
+
+Если ты будешь вести себя как сволочь - не удивляйся, что и к тебе будут относиться так же.
diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php
index 1ceac84..980a52b 100644
--- a/src/NXP/Classes/Calculator.php
+++ b/src/NXP/Classes/Calculator.php
@@ -36,25 +36,17 @@ class Calculator
{
$stack = [];
foreach ($tokens as $token) {
- if ($token instanceof TokenNumber) {
- array_push($stack, $token);
- }
- if ($token instanceof TokenStringDoubleQuoted) {
- array_push($stack, $token);
- }
- if ($token instanceof TokenStringSingleQuoted) {
- array_push($stack, $token);
- }
- if ($token instanceof TokenVariable) {
+ if ($token instanceof TokenNumber || $token instanceof TokenStringDoubleQuoted || $token instanceof TokenStringSingleQuoted) {
+ $stack[] = $token;
+ } else if ($token instanceof TokenVariable) {
$variable = $token->getValue();
if (!array_key_exists($variable, $variables)) {
throw new UnknownVariableException($variable);
}
$value = $variables[$variable];
- array_push($stack, new TokenNumber($value));
- }
- if ($token instanceof InterfaceOperator || $token instanceof TokenFunction) {
- array_push($stack, $token->execute($stack));
+ $stack[] = new TokenNumber($value);
+ } else if ($token instanceof InterfaceOperator || $token instanceof TokenFunction) {
+ $stack[] = $token->execute($stack);
}
}
$result = array_pop($stack);
diff --git a/src/NXP/Classes/Lexer.php b/src/NXP/Classes/Lexer.php
index a511c9b..898d8c1 100644
--- a/src/NXP/Classes/Lexer.php
+++ b/src/NXP/Classes/Lexer.php
@@ -77,7 +77,7 @@ class Lexer
} elseif ($token instanceof TokenVariable) {
$output[] = $token;
} elseif ($token instanceof TokenFunction) {
- array_push($stack, $token);
+ $stack[] = $token;
} elseif ($token instanceof AbstractOperator) {
// While we have something on the stack
while (($count = count($stack)) > 0
@@ -104,9 +104,12 @@ class Lexer
$output[] = array_pop($stack);
}
- array_push($stack, $token);
+ // Comma operators do nothing really, don't put them on the stack
+ if (! ($token instanceof TokenComma)) {
+ $stack[] = $token;
+ }
} elseif ($token instanceof TokenLeftBracket) {
- array_push($stack, $token);
+ $stack[] = $token;
} elseif ($token instanceof TokenRightBracket) {
while (($current = array_pop($stack)) && (!($current instanceof TokenLeftBracket))) {
$output[] = $current;
diff --git a/src/NXP/Classes/Token/TokenComma.php b/src/NXP/Classes/Token/TokenComma.php
index 6a45595..f6fc068 100644
--- a/src/NXP/Classes/Token/TokenComma.php
+++ b/src/NXP/Classes/Token/TokenComma.php
@@ -13,7 +13,7 @@ namespace NXP\Classes\Token;
/**
* @author Alexander Kiryukhin <a.kiryukhin@mail.ru>
*/
-class TokenComma implements InterfaceToken
+class TokenComma extends AbstractOperator
{
/**
* @return string
@@ -22,4 +22,32 @@ class TokenComma implements InterfaceToken
{
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/TokenFunction.php b/src/NXP/Classes/Token/TokenFunction.php
index d61bb5d..432f107 100644
--- a/src/NXP/Classes/Token/TokenFunction.php
+++ b/src/NXP/Classes/Token/TokenFunction.php
@@ -32,8 +32,9 @@ class TokenFunction extends AbstractContainerToken implements InterfaceFunction
$args = [];
list($places, $function) = $this->value;
for ($i = 0; $i < $places; $i++) {
- $args[] = array_shift($stack)->getValue();
+ array_unshift($args, array_pop($stack)->getValue());
}
+
$result = call_user_func_array($function, $args);
return new TokenNumber($result);
diff --git a/tests/MathTest.php b/tests/MathTest.php
index d1c9604..eba32c0 100644
--- a/tests/MathTest.php
+++ b/tests/MathTest.php
@@ -32,7 +32,7 @@ class MathTest extends \PHPUnit_Framework_TestCase
/** @var float $phpResult */
eval('$phpResult = ' . $expression . ';');
- $this->assertEquals($calculator->execute($expression), $phpResult, "Expression was: ${expression}");
+ $this->assertEquals($phpResult, $calculator->execute($expression), "Expression was: ${expression}");
}
/**
@@ -117,7 +117,7 @@ class MathTest extends \PHPUnit_Framework_TestCase
public function testZeroDivision()
{
$calculator = new MathExecutor();
- $this->assertEquals($calculator->execute('10 / 0'), 0);
+ $this->assertEquals(0, $calculator->execute('10 / 0'));
}
public function testZeroDivisionException()
@@ -131,19 +131,39 @@ class MathTest extends \PHPUnit_Framework_TestCase
public function testExponentiation()
{
$calculator = new MathExecutor();
- $this->assertEquals($calculator->execute('10 ^ 2'), 100);
+ $this->assertEquals(100, $calculator->execute('10 ^ 2'));
+ }
+
+ public function testFunctionParameterOrder()
+ {
+ $calculator = new MathExecutor();
+
+ $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')"));
}
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);
- }, 1);
- /** @var float $phpResult */
- eval('$phpResult = round(100/30);');
- $this->assertEquals($calculator->execute('round(100/30)'), $phpResult);
+ public function testEvaluateFunctionParameters()
+ {
+ $calculator = new MathExecutor();
+ $calculator->addFunction('round', function ($value, $decimals)
+ {
+ return round($value, $decimals);
+ }
+ );
+ $expression = 'round(100 * 1.111111, 2)';
+ eval('$phpResult = ' . $expression . ';');
+ $this->assertEquals($phpResult, $calculator->execute($expression));
+ $expression = 'round((100*0.04)+(((100*1.02)+0.5)*1.28),2)';
+ eval('$phpResult = ' . $expression . ';');
+ $this->assertEquals($phpResult, $calculator->execute($expression));
}
public function testFunctionsWithQuotes()
@@ -158,11 +178,13 @@ class MathTest extends \PHPUnit_Framework_TestCase
{
$calculator = new MathExecutor();
$testString = "some, long. arg; with: different-separators!";
- $calculator->addFunction('test', function ($arg) use ($testString) {
- $this->assertEquals($arg, $testString);
+ $calculator->addFunction('test', function ($arg) use ($testString)
+ {
+ $this->assertEquals($testString, $arg);
return 0;
- }, 1);
+ }
+ );
$calculator->execute('test("' . $testString . '")'); // single quotes
$calculator->execute("test('" . $testString . "')"); // double quotes
}
-} \ No newline at end of file
+}