aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml9
-rw-r--r--LICENSE19
-rw-r--r--NXP/Tests/MathTest.php64
-rw-r--r--README.md50
-rw-r--r--composer.json2
-rw-r--r--phpunit.xml.dist20
-rw-r--r--src/NXP/Classes/Func.php (renamed from NXP/Classes/Func.php)18
-rw-r--r--src/NXP/Classes/Operand.php (renamed from NXP/Classes/Operand.php)18
-rw-r--r--src/NXP/Classes/Token.php (renamed from NXP/Classes/Token.php)19
-rw-r--r--src/NXP/Classes/TokenParser.php (renamed from NXP/Classes/TokenParser.php)58
-rw-r--r--src/NXP/Exception/IncorrectExpressionException.php19
-rw-r--r--src/NXP/Exception/MathExecutorException.php19
-rw-r--r--src/NXP/Exception/UnknownFunctionException.php19
-rw-r--r--src/NXP/Exception/UnknownOperatorException.php19
-rw-r--r--src/NXP/Exception/UnknownTokenException.php19
-rw-r--r--src/NXP/MathExecutor.php (renamed from NXP/MathExecutor.php)195
-rw-r--r--test.php12
-rw-r--r--tests/MathTest.php51
-rw-r--r--tests/bootstrap.php11
19 files changed, 476 insertions, 165 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..842031b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+language: php
+
+php:
+ - 5.3
+ - 5.4
+
+before_script:
+ - wget http://getcomposer.org/composer.phar
+ - php composer.phar install \ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e45f709
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) Alexander Kiryukhin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/NXP/Tests/MathTest.php b/NXP/Tests/MathTest.php
deleted file mode 100644
index 6bfcf55..0000000
--- a/NXP/Tests/MathTest.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 14.03.13
- * Time: 3:41
- */
-namespace NXP\Tests;
-
-use \NXP\MathExecutor;
-
-class MathTest extends \PHPUnit_Framework_TestCase {
- public function setup()
- {
- require '../MathExecutor.php';
- require '../Classes/Func.php';
- require '../Classes/Operand.php';
- require '../Classes/Token.php';
- require '../Classes/TokenParser.php';
- }
- public function testCalculating()
- {
- $calculator = new MathExecutor();
- for ($i = 1; $i <= 10; $i++) {
- $expression = $this->generateExpression();
- print "Test #$i. Expression: '$expression'\t";
-
- eval('$result1 = ' . $expression . ';');
- print "PHP result: $result1 \t";
- $result2 = $calculator->execute($expression);
- print "NXP Math Executor result: $result2\n";
- $this->assertEquals($result1, $result2);
- }
- }
-
- private function generateExpression()
- {
- $operators = [ '+', '-', '*', '/' ];
- $number = true;
- $expression = '';
- $brackets = 0;
- for ($i = 1; $i < rand(1,10)*2; $i++) {
- if ($number) {
- $expression .= rand(1,100)*0.5;
- } else {
- $expression .= $operators[rand(0,3)];
- }
- $number = !$number;
- $rand = rand(1,5);
- if (($rand == 1) && ($number)) {
- $expression .= '(';
- $brackets++;
- } elseif (($rand == 2) && (!$number) && ($brackets > 0)) {
- $expression .= ')';
- $brackets--;
- }
- }
- if ($number) {
- $expression .= rand(1,100)*0.5;
- }
- $expression .= str_repeat(')', $brackets);
-
- return $expression;
- }
-} \ No newline at end of file
diff --git a/README.md b/README.md
index 8cbbaa8..ca5ea65 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,49 @@
Simple math expressions calculator
-## Sample usage:
-
- <?php
- require "vendor/autoload.php";
- $calculator = new \NXP\MathExecutor();
- print $calculator->execute("1 + 2 * (2 - (4+10))^2");
-
## Install via Composer
All instructions to install here: https://packagist.org/packages/nxp/math-executor
+
+## Sample usage:
+
+```php
+require "vendor/autoload.php";
+
+$calculator = new \NXP\MathExecutor();
+
+print $calculator->execute("1 + 2 * (2 - (4+10))^2");
+```
+
+## Functions:
+
+Default functions:
+* sin
+* cos
+* tn
+* asin
+* asoc
+* atn
+
+Add custom function to executor:
+```php
+$executor->addFunction('abs', function($arg) {
+ return abs($arg);
+});
+```
+
+## Operators:
+
+Default operators: `+ - * / ^`
+
+## Variables:
+
+You can add own variable to executor:
+
+```php
+$executor->setVars(array(
+ 'var1' => 0.15,
+ 'var2' => 0.22
+));
+
+$executor->execute("var1 + var2"); \ No newline at end of file
diff --git a/composer.json b/composer.json
index 701b47c..586ad96 100644
--- a/composer.json
+++ b/composer.json
@@ -12,6 +12,6 @@
}
],
"autoload": {
- "psr-0": {"NXP": "."}
+ "psr-0": {"NXP": "src/"}
}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..bfe1846
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="./tests/bootstrap.php"
+ >
+
+ <testsuites>
+ <testsuite name="Math Executor tests">
+ <directory>./tests/</directory>
+ </testsuite>
+ </testsuites>
+</phpunit> \ No newline at end of file
diff --git a/NXP/Classes/Func.php b/src/NXP/Classes/Func.php
index b3e5b93..e8c0fa2 100644
--- a/NXP/Classes/Func.php
+++ b/src/NXP/Classes/Func.php
@@ -1,14 +1,18 @@
<?php
+
/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 17.03.13
- * Time: 4:30
+ * 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;
-
-class Func {
+class Func
+{
/**
* @var string
*/
@@ -23,7 +27,7 @@ class Func {
* @param $name
* @param $callback
*/
- function __construct($name, $callback)
+ public function __construct($name, $callback)
{
$this->name = $name;
$this->callback = $callback;
@@ -38,4 +42,4 @@ class Func {
{
return $this->callback;
}
-} \ No newline at end of file
+}
diff --git a/NXP/Classes/Operand.php b/src/NXP/Classes/Operand.php
index 3329679..fae0c69 100644
--- a/NXP/Classes/Operand.php
+++ b/src/NXP/Classes/Operand.php
@@ -1,14 +1,18 @@
<?php
+
/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 17.03.13
- * Time: 4:27
+ * 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;
-
-class Operand {
+class Operand
+{
const LEFT_ASSOCIATED = 'LEFT_ASSOCIATED';
const RIGHT_ASSOCIATED = 'RIGHT_ASSOCIATED';
const ASSOCIATED = 'ASSOCIATED';
@@ -48,7 +52,7 @@ class Operand {
* @param $type
* @param $callback
*/
- function __construct($symbol, $priority, $association, $type, $callback)
+ public function __construct($symbol, $priority, $association, $type, $callback)
{
$this->association = $association;
$this->symbol = $symbol;
@@ -82,4 +86,4 @@ class Operand {
return $this->priority;
}
-} \ No newline at end of file
+}
diff --git a/NXP/Classes/Token.php b/src/NXP/Classes/Token.php
index 28bc736..bbcd5bc 100644
--- a/NXP/Classes/Token.php
+++ b/src/NXP/Classes/Token.php
@@ -1,15 +1,18 @@
<?php
+
/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 17.03.13
- * Time: 3:23
+ * 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;
-
-class Token {
-
+class Token
+{
const NOTHING = 'NOTHING';
const STRING = 'STRING';
const NUMBER = 'NUMBER';
@@ -28,7 +31,7 @@ class Token {
*/
protected $type;
- function __construct($type, $value)
+ public function __construct($type, $value)
{
$this->type = $type;
$this->value = $value;
@@ -50,4 +53,4 @@ class Token {
return $this->value;
}
-} \ No newline at end of file
+}
diff --git a/NXP/Classes/TokenParser.php b/src/NXP/Classes/TokenParser.php
index 7b9255f..f498184 100644
--- a/NXP/Classes/TokenParser.php
+++ b/src/NXP/Classes/TokenParser.php
@@ -1,14 +1,18 @@
<?php
+
/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 17.03.13
- * Time: 2:45
+ * 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;
-
-class TokenParser {
+class TokenParser
+{
const DIGIT = 'DIGIT';
const CHAR = 'CHAR';
const SPECIAL_CHAR = 'SPECIAL_CHAR';
@@ -16,75 +20,75 @@ class TokenParser {
const RIGHT_BRACKET = 'RIGHT_BRACKET';
const SPACE = 'SPACE';
- private $terms = [
+ private $terms = array(
self::DIGIT => '[0-9\.]',
- self::CHAR => '[a-z]',
+ self::CHAR => '[a-z_]',
self::SPECIAL_CHAR => '[\!\@\#\$\%\^\&\*\/\|\-\+\=\~]',
self::LEFT_BRACKET => '\(',
self::RIGHT_BRACKET => '\)',
self::SPACE => '\s'
- ];
+ );
const ERROR_STATE = 'ERROR_STATE';
- private $transitions = [
- Token::NOTHING => [
+ private $transitions = array(
+ Token::NOTHING => array(
self::DIGIT => Token::NUMBER,
self::CHAR => Token::STRING,
self::SPECIAL_CHAR => Token::OPERATOR,
self::LEFT_BRACKET => Token::LEFT_BRACKET,
self::RIGHT_BRACKET => Token::RIGHT_BRACKET,
self::SPACE => Token::NOTHING
- ],
- Token::STRING => [
+ ),
+ Token::STRING => array(
self::DIGIT => Token::STRING,
self::CHAR => Token::STRING,
self::SPECIAL_CHAR => Token::OPERATOR,
self::LEFT_BRACKET => Token::LEFT_BRACKET,
self::RIGHT_BRACKET => Token::RIGHT_BRACKET,
self::SPACE => Token::NOTHING
- ],
- Token::NUMBER => [
+ ),
+ Token::NUMBER => array(
self::DIGIT => Token::NUMBER,
self::CHAR => self::ERROR_STATE,
self::SPECIAL_CHAR => Token::OPERATOR,
self::LEFT_BRACKET => Token::LEFT_BRACKET,
self::RIGHT_BRACKET => Token::RIGHT_BRACKET,
self::SPACE => Token::NOTHING
- ],
- Token::OPERATOR => [
+ ),
+ Token::OPERATOR => array(
self::DIGIT => Token::NUMBER,
self::CHAR => Token::STRING,
self::SPECIAL_CHAR => Token::OPERATOR,
self::LEFT_BRACKET => Token::LEFT_BRACKET,
self::RIGHT_BRACKET => Token::RIGHT_BRACKET,
self::SPACE => Token::NOTHING
- ],
- self::ERROR_STATE => [
+ ),
+ self::ERROR_STATE => array(
self::DIGIT => self::ERROR_STATE,
self::CHAR => self::ERROR_STATE,
self::SPECIAL_CHAR => self::ERROR_STATE,
self::LEFT_BRACKET => self::ERROR_STATE,
self::RIGHT_BRACKET => self::ERROR_STATE,
self::SPACE => self::ERROR_STATE
- ],
- Token::LEFT_BRACKET => [
+ ),
+ Token::LEFT_BRACKET => array(
self::DIGIT => Token::NUMBER,
self::CHAR => Token::STRING,
self::SPECIAL_CHAR => Token::OPERATOR,
self::LEFT_BRACKET => Token::LEFT_BRACKET,
self::RIGHT_BRACKET => Token::RIGHT_BRACKET,
self::SPACE => Token::NOTHING
- ],
- Token::RIGHT_BRACKET => [
+ ),
+ Token::RIGHT_BRACKET => array(
self::DIGIT => Token::NUMBER,
self::CHAR => Token::STRING,
self::SPECIAL_CHAR => Token::OPERATOR,
self::LEFT_BRACKET => Token::LEFT_BRACKET,
self::RIGHT_BRACKET => Token::RIGHT_BRACKET,
self::SPACE => Token::NOTHING
- ],
- ];
+ ),
+ );
private $accumulator = '';
@@ -92,7 +96,7 @@ class TokenParser {
private $queue = null;
- function __construct()
+ public function __construct()
{
$this->queue = new \SplQueue();
}
@@ -148,12 +152,14 @@ class TokenParser {
{
if ($oldState == Token::NOTHING) {
$this->accumulator = '';
+
return;
}
+
if (($this->state != $oldState) || ($oldState == Token::LEFT_BRACKET) || ($oldState == Token::RIGHT_BRACKET)) {
$token = new Token($oldState, $this->accumulator);
$this->queue->push($token);
$this->accumulator = '';
}
}
-} \ No newline at end of file
+}
diff --git a/src/NXP/Exception/IncorrectExpressionException.php b/src/NXP/Exception/IncorrectExpressionException.php
new file mode 100644
index 0000000..ad5bc42
--- /dev/null
+++ b/src/NXP/Exception/IncorrectExpressionException.php
@@ -0,0 +1,19 @@
+<?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 IncorrectExpressionException extends \Exception
+{
+}
diff --git a/src/NXP/Exception/MathExecutorException.php b/src/NXP/Exception/MathExecutorException.php
new file mode 100644
index 0000000..0e3ea84
--- /dev/null
+++ b/src/NXP/Exception/MathExecutorException.php
@@ -0,0 +1,19 @@
+<?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>
+ */
+abstract class MathExecutorException extends \Exception
+{
+}
diff --git a/src/NXP/Exception/UnknownFunctionException.php b/src/NXP/Exception/UnknownFunctionException.php
new file mode 100644
index 0000000..5bb3658
--- /dev/null
+++ b/src/NXP/Exception/UnknownFunctionException.php
@@ -0,0 +1,19 @@
+<?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 UnknownFunctionException extends \Exception
+{
+}
diff --git a/src/NXP/Exception/UnknownOperatorException.php b/src/NXP/Exception/UnknownOperatorException.php
new file mode 100644
index 0000000..b6617c3
--- /dev/null
+++ b/src/NXP/Exception/UnknownOperatorException.php
@@ -0,0 +1,19 @@
+<?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 UnknownOperatorException extends \Exception
+{
+}
diff --git a/src/NXP/Exception/UnknownTokenException.php b/src/NXP/Exception/UnknownTokenException.php
new file mode 100644
index 0000000..b8a593f
--- /dev/null
+++ b/src/NXP/Exception/UnknownTokenException.php
@@ -0,0 +1,19 @@
+<?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 \Exception
+{
+}
diff --git a/NXP/MathExecutor.php b/src/NXP/MathExecutor.php
index cc1c187..482b4b7 100644
--- a/NXP/MathExecutor.php
+++ b/src/NXP/MathExecutor.php
@@ -1,28 +1,51 @@
<?php
+
/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 14.03.13
- * Time: 1:01
+ * 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;
use NXP\Classes\Func;
use NXP\Classes\Operand;
use NXP\Classes\Token;
use NXP\Classes\TokenParser;
+use NXP\Exception\IncorrectExpressionException;
+use NXP\Exception\UnknownFunctionException;
+use NXP\Exception\UnknownOperatorException;
+use NXP\Exception\UnknownTokenException;
/**
* Class MathExecutor
* @package NXP
*/
-class MathExecutor {
-
-
- private $operators = [ ];
+class MathExecutor
+{
+ /**
+ * Available operators
+ *
+ * @var array
+ */
+ private $operators = array();
- private $functions = [ ];
+ /**
+ * Available functions
+ *
+ * @var array
+ */
+ private $functions = array();
- private $variables = [ ];
+ /**
+ * Available variables
+ *
+ * @var array
+ */
+ private $variables = array();
/**
* @var \SplStack
@@ -39,6 +62,23 @@ class MathExecutor {
*/
public function __construct()
{
+ $this->addDefaults();
+ }
+
+ public function __clone()
+ {
+ $this->variables = array();
+ $this->operators = array();
+ $this->functions = array();
+
+ $this->addDefaults();
+ }
+
+ /**
+ * Set default operands and functions
+ */
+ protected function addDefaults()
+ {
$this->addOperator(new Operand('+', 1, Operand::LEFT_ASSOCIATED, Operand::BINARY, function ($op1, $op2) { return $op1+$op2; }));
$this->addOperator(new Operand('-', 1, Operand::LEFT_ASSOCIATED, Operand::BINARY, function ($op1, $op2) { return $op1-$op2; }));
$this->addOperator(new Operand('*', 2, Operand::LEFT_ASSOCIATED, Operand::BINARY, function ($op1, $op2) { return $op1*$op2; }));
@@ -55,38 +95,100 @@ class MathExecutor {
/**
* Add operator to executor
- * @param Operand $operator
+ *
+ * @param Operand $operator
+ * @return MathExecutor
*/
public function addOperator(Operand $operator)
{
$this->operators[$operator->getSymbol()] = $operator;
+
+ return $this;
}
/**
* Add function to executor
- * @param Func $function
+ *
+ * @param string $name
+ * @param callable $function
+ * @return MathExecutor
*/
- public function addFunction(Func $function)
+ public function addFunction($name, callable $function = null)
{
- $this->functions[$function->getName()] = $function->getCallback();
+ if ($name instanceof Func) {
+ $this->functions[$name->getName()] = $name->getCallback();
+ } else {
+ $this->functions[$name] = $function;
+ }
+
+ return $this;
}
/**
* Add variable to executor
- * @param $variable
- * @param $value
+ *
+ * @param string $variable
+ * @param integer|float $value
* @throws \Exception
+ * @return MathExecutor
*/
public function setVar($variable, $value)
{
if (!is_numeric($value)) {
throw new \Exception("Variable value must be a number");
}
+
$this->variables[$variable] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Add variables to executor
+ *
+ * @param array $variables
+ * @param bool $clear Clear previous variables
+ * @return MathExecutor
+ */
+ public function setVars(array $variables, $clear = true)
+ {
+ if ($clear) {
+ $this->removeVars();
+ }
+
+ foreach ($variables as $name => $value) {
+ $this->setVar($name, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Remove variable from executor
+ *
+ * @param string $variable
+ * @return MathExecutor
+ */
+ public function removeVar($variable)
+ {
+ unset ($this->variables[$variable]);
+
+ return $this;
+ }
+
+ /**
+ * Remove all variables
+ */
+ public function removeVars()
+ {
+ $this->variables = array();
+
+ return $this;
}
/**
* Execute expression
+ *
* @param $expression
* @return int|float
*/
@@ -100,6 +202,7 @@ class MathExecutor {
/**
* Convert expression from normal expression form to RPN
+ *
* @param $expression
* @return \SplQueue
* @throws \Exception
@@ -118,9 +221,11 @@ class MathExecutor {
while (!$this->stack->isEmpty()) {
$token = $this->stack->pop();
+
if ($token->getType() != Token::OPERATOR) {
throw new \Exception('Opening bracket without closing bracket');
}
+
$this->queue->push($token);
}
@@ -128,7 +233,7 @@ class MathExecutor {
}
/**
- * @param Token $token
+ * @param Token $token
* @throws \Exception
*/
private function categorizeToken(Token $token)
@@ -157,17 +262,23 @@ class MathExecutor {
$previousToken = $this->stack->pop();
}
if ((!$this->stack->isEmpty()) && ($this->stack->top()->getType() == Token::STRING)) {
- $string = $this->stack->pop()->getValue();
- if (!array_key_exists($string, $this->functions)) {
- throw new \Exception('Unknown function');
+ $funcName = $this->stack->pop()->getValue();
+ if (!array_key_exists($funcName, $this->functions)) {
+ throw new UnknownFunctionException(sprintf(
+ 'Unknown function: "%s".',
+ $funcName
+ ));
}
- $this->queue->push(new Token(Token::FUNC, $string));
+ $this->queue->push(new Token(Token::FUNC, $funcName));
}
break;
case Token::OPERATOR:
if (!array_key_exists($token->getValue(), $this->operators)) {
- throw new \Exception("Unknown operator '{$token->getValue()}'");
+ throw new UnknownOperatorException(sprintf(
+ 'Unknown operator: "%s".',
+ $token->getValue()
+ ));
}
$this->proceedOperator($token);
@@ -175,7 +286,10 @@ class MathExecutor {
break;
default:
- throw new \Exception('Unknown token');
+ throw new UnknownTokenException(sprintf(
+ 'Unknown token: "%s".',
+ $token->getValue()
+ ));
}
}
@@ -183,17 +297,25 @@ class MathExecutor {
* @param $token
* @throws \Exception
*/
- private function proceedOperator($token)
+ private function proceedOperator(Token $token)
{
if (!array_key_exists($token->getValue(), $this->operators)) {
- throw new \Exception('Unknown operator');
+ throw new UnknownOperatorException(sprintf(
+ 'Unknown operator: "%s".',
+ $token->getValue()
+ ));
}
+
/** @var Operand $operator */
$operator = $this->operators[$token->getValue()];
+
while (!$this->stack->isEmpty()) {
$top = $this->stack->top();
+
if ($top->getType() == Token::OPERATOR) {
- $priority = $this->operators[$top->getValue()]->getPriority();
+ /** @var Operand $operator */
+ $operator = $this->operators[$top->getValue()];
+ $priority = $operator->getPriority();
if ( $operator->getAssociation() == Operand::RIGHT_ASSOCIATED) {
if (($priority > $operator->getPriority())) {
$this->queue->push($this->stack->pop());
@@ -216,19 +338,20 @@ class MathExecutor {
}
/**
- * @param \SplQueue $expression
+ * @param \SplQueue $expression
* @return mixed
* @throws \Exception
*/
private function calculateReversePolishNotation(\SplQueue $expression)
{
$this->stack = new \SplStack();
- /** @val Token $token */
+ /** @var Token $token */
foreach ($expression as $token) {
switch ($token->getType()) {
case Token::NUMBER :
$this->stack->push($token);
break;
+
case Token::OPERATOR:
/** @var Operand $operator */
$operator = $this->operators[$token->getValue()];
@@ -241,24 +364,30 @@ class MathExecutor {
}
$callback = $operator->getCallback();
-
- $this->stack->push(new Token(Token::NUMBER, ($callback($arg1, $arg2))));
+ $this->stack->push(new Token(Token::NUMBER, (call_user_func($callback, $arg1, $arg2))));
break;
+
case Token::FUNC:
/** @var Func $function */
$callback = $this->functions[$token->getValue()];
$arg = $this->stack->pop()->getValue();
- $this->stack->push(new Token(Token::NUMBER, ($callback($arg))));
+ $this->stack->push(new Token(Token::NUMBER, (call_user_func($callback, $arg))));
break;
+
default:
- throw new \Exception('Unknown token');
+ throw new UnknownTokenException(sprintf(
+ 'Unknown token: "%s".',
+ $token->getValue()
+ ));
}
}
+
$result = $this->stack->pop()->getValue();
+
if (!$this->stack->isEmpty()) {
- throw new \Exception('Incorrect expression');
+ throw new IncorrectExpressionException('Incorrect expression.');
}
return $result;
}
-} \ No newline at end of file
+}
diff --git a/test.php b/test.php
deleted file mode 100644
index 2e5d0e7..0000000
--- a/test.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-/**
- * Author: Alexander "NeonXP" Kiryukhin
- * Date: 14.03.13
- * Time: 1:08
- */
-require "vendor/autoload.php";
-
-$e = new \NXP\MathExecutor();
-
-$r = $e->execute("1 + 2 * (2 - (4+10))^2");
-var_dump($r);
diff --git a/tests/MathTest.php b/tests/MathTest.php
new file mode 100644
index 0000000..ba944fe
--- /dev/null
+++ b/tests/MathTest.php
@@ -0,0 +1,51 @@
+<?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\Tests;
+
+use \NXP\MathExecutor;
+
+class MathTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider providerExpressions
+ */
+ public function testCalculating($expression)
+ {
+ $calculator = new MathExecutor();
+
+ /** @var float $phpResult */
+ eval('$phpResult = ' . $expression . ';');
+ $this->assertEquals($calculator->execute($expression), $phpResult);
+ }
+
+ /**
+ * Expressions data provider
+ */
+ public function providerExpressions()
+ {
+ return array(
+ array('0.1 + 0.2'),
+ array('1 + 2'),
+
+ array('0.1 - 0.2'),
+ array('1 - 2'),
+
+ array('0.1 * 2'),
+ array('1 * 2'),
+
+ array('0.1 / 0.2'),
+ array('1 / 2'),
+
+ array('1 + 0.6 - (3 * 2 / 50)')
+ );
+ }
+} \ No newline at end of file
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..8eb3e7a
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,11 @@
+<?php
+
+$vendorDir = __DIR__ . '/../../..';
+
+if (file_exists($file = $vendorDir . '/autoload.php')) {
+ require_once $file;
+} else if (file_exists($file = './vendor/autoload.php')) {
+ require_once $file;
+} else {
+ throw new \RuntimeException("Not found composer autoload");
+} \ No newline at end of file