-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a Lexer/Parser for mathematical expressions. (Input Processor + Separate Action & Condition) #183
base: 8.x-3.x
Are you sure you want to change the base?
Add a Lexer/Parser for mathematical expressions. (Input Processor + Separate Action & Condition) #183
Changes from all commits
21010e3
da8436f
7e5ce4c
4f89f7c
ceecd04
bbc713f
d421c67
32b2f87
39ec925
bb8cecc
f372ec9
0ebc539
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Calculator. | ||
*/ | ||
|
||
namespace Drupal\rules\Math; | ||
|
||
use Drupal\rules\Math\Exception\IncorrectExpressionException; | ||
use Drupal\rules\Math\Exception\UnknownVariableException; | ||
use Drupal\rules\Math\Token\FunctionToken; | ||
use Drupal\rules\Math\Token\NumberToken; | ||
use Drupal\rules\Math\Token\OperatorTokenInterface; | ||
use Drupal\rules\Math\Token\VariableToken; | ||
|
||
/** | ||
* Parser for mathematical expressions. | ||
*/ | ||
class Calculator { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a docblock |
||
|
||
/** | ||
* Static cache of token streams in reverse polish (postfix) notation. | ||
* | ||
* @var array | ||
*/ | ||
protected $tokenCache = []; | ||
|
||
/** | ||
* Constructs a new Calculator object. | ||
*/ | ||
public function __construct() { | ||
$this->lexer = new Lexer(); | ||
|
||
$this->lexer | ||
->addOperator('plus', '\+', 'Drupal\rules\Math\Token\PlusToken') | ||
->addOperator('minus', '\-', 'Drupal\rules\Math\Token\MinusToken') | ||
->addOperator('multiply', '\*', 'Drupal\rules\Math\Token\MultiplyToken') | ||
->addOperator('division', '\/', 'Drupal\rules\Math\Token\DivisionToken') | ||
->addOperator('modulus', '\%', 'Drupal\rules\Math\Token\ModulusToken') | ||
->addOperator('power', '\^', 'Drupal\rules\Math\Token\PowerToken'); | ||
|
||
$this->lexer | ||
->addFunction('abs', 'abs', 1) | ||
->addFunction('acos', 'acos', 1) | ||
->addFunction('acosh', 'acosh', 1) | ||
->addFunction('asin', 'asin', 1) | ||
->addFunction('asinh', 'asinh', 1) | ||
->addFunction('atan2', 'atan2', 2) | ||
->addFunction('atan', 'atan', 1) | ||
->addFunction('atanh', 'atanh', 1) | ||
->addFunction('ceil', 'ceil', 1) | ||
->addFunction('cos', 'cos', 1) | ||
->addFunction('cosh', 'cosh', 1) | ||
->addFunction('deg2rad', 'deg2rad', 1) | ||
->addFunction('exp', 'exp', 1) | ||
->addFunction('floor', 'floor', 1) | ||
->addFunction('hypot', 'hypot', 2) | ||
->addFunction('log10', 'log10', 1) | ||
->addFunction('log', 'log', 2) | ||
->addFunction('max', 'max', 2) | ||
->addFunction('min', 'min', 2) | ||
->addFunction('pow', 'pow', 2) | ||
->addFunction('rad2deg', 'rad2deg', 1) | ||
->addFunction('rand', 'rand', 2) | ||
->addFunction('round', 'round', 1) | ||
->addFunction('sin', 'sin', 1) | ||
->addFunction('sinh', 'sinh', 1) | ||
->addFunction('sqrt', 'sqrt', 1) | ||
->addFunction('tan', 'tan', 1) | ||
->addFunction('tanh', 'tanh', 1); | ||
|
||
$this->lexer | ||
->addConstant('pi', pi()) | ||
->addConstant('e', exp(1)); | ||
} | ||
|
||
/** | ||
* Calculates the result of a mathematical expression. | ||
* | ||
* @param string $expression | ||
* The mathematical expression. | ||
* @param array $variables | ||
* A list of numerical values keyed by their variable names. | ||
* | ||
* @return mixed | ||
* The result of the mathematical expression. | ||
* | ||
* @throws \Drupal\rules\Math\Exception\IncorrectExpressionException | ||
* @throws \Drupal\rules\Math\Exception\IncorrectParenthesisException | ||
*/ | ||
public function calculate($expression, $variables) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's document this |
||
$hash = md5($expression); | ||
if (isset($this->tokenCache[$hash])) { | ||
return $this->tokenCache[$hash]; | ||
} | ||
|
||
$stream = $this->lexer->tokenize($expression); | ||
$tokens = $this->lexer->postfix($stream); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So after the postfix, tokens will be in reverse polish notation. Which is the reason why you can use a stack to evaluate. Would be cool to explain this a bit more |
||
$this->tokenCache[$hash] = $tokens; | ||
|
||
$stack = []; | ||
foreach ($tokens as $token) { | ||
if ($token instanceof NumberToken) { | ||
array_push($stack, $token); | ||
} | ||
elseif ($token instanceof VariableToken) { | ||
$identifier = $token->getValue(); | ||
if (!isset($variables[$identifier])) { | ||
throw new UnknownVariableException($token->getOffset(), $identifier); | ||
} | ||
array_push($stack, new NumberToken($token->getOffset(), $variables[$identifier])); | ||
} | ||
elseif ($token instanceof OperatorTokenInterface || $token instanceof FunctionToken) { | ||
array_push($stack, $token->execute($stack)); | ||
} | ||
} | ||
|
||
$result = array_pop($stack); | ||
if (!empty($stack)) { | ||
throw new IncorrectExpressionException(); | ||
} | ||
|
||
return $result->getValue(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\IncorrectExpressionException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class IncorrectExpressionException extends RulesException { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\IncorrectParenthesisException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class IncorrectParenthesisException extends RulesException { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\UnknownConstantException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class UnknownConstantException extends RulesException { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\UnknownFunctionException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class UnknownFunctionException extends RulesException { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\UnknownOperatorException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class UnknownOperatorException extends RulesException { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\UnknownTokenException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class UnknownTokenException extends RulesException { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* Contains \Drupal\rules\Math\Exception\UnknownVariableException. | ||
*/ | ||
|
||
namespace Drupal\rules\Math\Exception; | ||
|
||
use Drupal\rules\Exception\RulesException; | ||
|
||
class UnknownVariableException extends RulesException { | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs @file