Skip to content

Commit

Permalink
Handler for not found variables (#68)
Browse files Browse the repository at this point in the history
* Added handler to define not found variables
Added support for string variables
Fixed strings and ints comparison error

* Check if variables have scalar types (int, float, string and bool)
Better $onVarNotFound logic
  • Loading branch information
javiermarinros authored Jul 26, 2020
1 parent aa8ffe1 commit c1e07f2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 14 deletions.
14 changes: 11 additions & 3 deletions src/NXP/Classes/Calculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function __construct(array $functions, array $operators)
* @throws IncorrectExpressionException
* @throws UnknownVariableException
*/
public function calculate(array $tokens, array $variables)
public function calculate(array $tokens, array $variables, callable $onVarNotFound = null)
{
/** @var Token[] $stack */
$stack = [];
Expand All @@ -58,10 +58,18 @@ public function calculate(array $tokens, array $variables)
$stack[] = $token;
} elseif ($token->type === Token::Variable) {
$variable = $token->value;
if (!array_key_exists($variable, $variables)) {

$value = null;
if (array_key_exists($variable, $variables)) {
$value = $variables[$variable];
} elseif ($onVarNotFound) {
$value = call_user_func($onVarNotFound, $variable);
}

if (!isset($value)) {
throw new UnknownVariableException($variable);
}
$value = $variables[$variable];

$stack[] = new Token(Token::Literal, $value);
} elseif ($token->type === Token::Function) {
if (!array_key_exists($token->value, $this->functions)) {
Expand Down
2 changes: 1 addition & 1 deletion src/NXP/Exception/MathExecutorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
/**
* @author Vitaliy Zhuk <[email protected]>
*/
abstract class MathExecutorException extends \Exception
class MathExecutorException extends \Exception
{
}
48 changes: 38 additions & 10 deletions src/NXP/MathExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
use NXP\Classes\CustomFunction;
use NXP\Classes\Operator;
use NXP\Classes\Tokenizer;
use NXP\Exception\MathExecutorException;
use NXP\Exception\DivisionByZeroException;
use NXP\Exception\UnknownVariableException;
use NXP\Exception\MathExecutorException;
use ReflectionException;

/**
Expand All @@ -33,6 +33,11 @@ class MathExecutor
*/
private $variables = [];

/**
* @var callable
*/
private $onVarNotFound = null;

/**
* @var Operator[]
*/
Expand Down Expand Up @@ -134,14 +139,22 @@ function ($a, $b) {
],
'==' => [
function ($a, $b) {
return $a == $b;
if (is_string($a) || is_string($b)) {
return strcmp($a, $b) == 0;
} else {
return $a == $b;
}
},
140,
false
],
'!=' => [
function ($a, $b) {
return $a != $b;
if (is_string($a) || is_string($b)) {
return strcmp($a, $b) != 0;
} else {
return $a != $b;
}
},
140,
false
Expand Down Expand Up @@ -353,7 +366,7 @@ public function execute(string $expression)
$tokens = $this->cache[$cachekey];
}
$calculator = new Calculator($this->functions, $this->operators);
return $calculator->calculate($tokens, $this->variables);
return $calculator->calculate($tokens, $this->variables, $this->onVarNotFound);
}

/**
Expand Down Expand Up @@ -415,13 +428,13 @@ public function getVar(string $variable)
* @param string $variable
* @param integer|float $value
* @return MathExecutor
* @throws MathExecutorException
*/
public function setVar(string $variable, $value) : self
{
if (!is_numeric($value)) {
throw new MathExecutorException("Variable ({$variable}) value must be a number ({$value}) type ({gettype($value)})");
if (!is_scalar($value)) {
throw new MathExecutorException("Variable ({$variable}) value must be a scalar type ({gettype($value)})");
}

$this->variables[$variable] = $value;
return $this;
}
Expand All @@ -445,6 +458,20 @@ public function setVars(array $variables, bool $clear = true) : self
return $this;
}

/**
* Define a method that will be invoked when a variable is not found.
* The first parameter will be the variable name, and the returned value will be used as the variable value.
*
* @param callable $handler
*
* @return MathExecutor
*/
public function setVarNotFoundHandler(callable $handler): self
{
$this->onVarNotFound = $handler;
return $this;
}

/**
* Remove variable from executor
*
Expand All @@ -458,12 +485,13 @@ public function removeVar(string $variable) : self
}

/**
* Remove all variables
* Remove all variables and the variable not found handler
* @return MathExecutor
*/
public function removeVars() : self
{
$this->variables = [];
$this->onVarNotFound = null;
return $this;
}

Expand All @@ -474,7 +502,7 @@ public function removeVars() : self
*/
public function getOperators()
{
return $this->tokenFactory->getOperators();
return $this->operators;
}

/**
Expand All @@ -485,7 +513,7 @@ public function getOperators()
*/
public function getFunctions() : array
{
return $this->tokenFactory->getFunctions();
return $this->functions;
}

/**
Expand Down
31 changes: 31 additions & 0 deletions tests/MathTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,37 @@ public function testBeginWithBracketAndMinus()
$this->assertEquals(1, $calculator->execute('(-4 + 5)'));
}

public function testStringComparison()
{
$calculator = new MathExecutor();
$this->assertEquals(true, $calculator->execute('"a" == \'a\''));
$this->assertEquals(true, $calculator->execute('"hello world" == "hello world"'));
$this->assertEquals(false, $calculator->execute('"hello world" == "hola mundo"'));
$this->assertEquals(true, $calculator->execute('"hello world" != "hola mundo"'));
}

public function testVarStringComparison()
{
$calculator = new MathExecutor();
$calculator->setVar('var', 0);
$this->assertEquals($calculator->execute('0 == "a"'), $calculator->execute('var == "a"'));
}

public function testOnVarNotFound()
{
$calculator = new MathExecutor();
$calculator->setVarNotFoundHandler(
function ($varName) {
if ($varName == 'undefined') {
return 3;
} else {
return null;
}
}
);
$this->assertEquals(15, $calculator->execute('5 * undefined'));
}

public function testMinusZero()
{
$calculator = new MathExecutor();
Expand Down

0 comments on commit c1e07f2

Please sign in to comment.