aboutsummaryrefslogtreecommitdiff
path: root/src/NXP/Classes
diff options
context:
space:
mode:
Diffstat (limited to 'src/NXP/Classes')
-rw-r--r--src/NXP/Classes/Calculator.php42
-rw-r--r--src/NXP/Classes/CustomFunction.php24
-rw-r--r--src/NXP/Classes/Operator.php33
-rw-r--r--src/NXP/Classes/Token.php41
-rw-r--r--src/NXP/Classes/Tokenizer.php252
5 files changed, 198 insertions, 194 deletions
diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php
index a8f16af..2bb5b4d 100644
--- a/src/NXP/Classes/Calculator.php
+++ b/src/NXP/Classes/Calculator.php
@@ -20,21 +20,10 @@ use NXP\Exception\UnknownVariableException;
*/
class Calculator
{
- /**
- * @var CustomFunction[]
- */
- private $functions;
+ private array $functions = [];
- /**
- * @var Operator[]
- */
- private $operators;
+ private array $operators = [];
- /**
- * Calculator constructor.
- * @param CustomFunction[] $functions
- * @param Operator[] $operators
- */
public function __construct(array $functions, array $operators)
{
$this->functions = $functions;
@@ -45,46 +34,49 @@ class Calculator
* Calculate array of tokens in reverse polish notation
* @param Token[] $tokens
* @param array<string, float|string> $variables
- * @return mixed
* @throws IncorrectExpressionException
* @throws UnknownVariableException
*/
- public function calculate(array $tokens, array $variables, callable $onVarNotFound = null)
+ public function calculate(array $tokens, array $variables, ?callable $onVarNotFound = null)
{
/** @var Token[] $stack */
$stack = [];
+
foreach ($tokens as $token) {
- if ($token->type === Token::Literal || $token->type === Token::String) {
+ if (Token::Literal === $token->type || Token::String === $token->type) {
$stack[] = $token;
- } elseif ($token->type === Token::Variable) {
+ } elseif (Token::Variable === $token->type) {
$variable = $token->value;
$value = null;
- if (array_key_exists($variable, $variables)) {
+
+ if (\array_key_exists($variable, $variables)) {
$value = $variables[$variable];
} elseif ($onVarNotFound) {
- $value = call_user_func($onVarNotFound, $variable);
+ $value = \call_user_func($onVarNotFound, $variable);
} else {
throw new UnknownVariableException($variable);
}
$stack[] = new Token(Token::Literal, $value, $variable);
- } elseif ($token->type === Token::Function) {
- if (!array_key_exists($token->value, $this->functions)) {
+ } elseif (Token::Function === $token->type) {
+ if (! \array_key_exists($token->value, $this->functions)) {
throw new UnknownFunctionException($token->value);
}
$stack[] = $this->functions[$token->value]->execute($stack);
- } elseif ($token->type === Token::Operator) {
- if (!array_key_exists($token->value, $this->operators)) {
+ } elseif (Token::Operator === $token->type) {
+ if (! \array_key_exists($token->value, $this->operators)) {
throw new UnknownOperatorException($token->value);
}
$stack[] = $this->operators[$token->value]->execute($stack);
}
}
- $result = array_pop($stack);
- if ($result === null || !empty($stack)) {
+ $result = \array_pop($stack);
+
+ if (null === $result || ! empty($stack)) {
throw new IncorrectExpressionException('Stack must be empty');
}
+
return $result->value;
}
}
diff --git a/src/NXP/Classes/CustomFunction.php b/src/NXP/Classes/CustomFunction.php
index 225495f..bc0f4e7 100644
--- a/src/NXP/Classes/CustomFunction.php
+++ b/src/NXP/Classes/CustomFunction.php
@@ -8,25 +8,17 @@ use ReflectionFunction;
class CustomFunction
{
- /**
- * @var string
- */
- public $name;
+ public string $name = '';
/**
* @var callable $function
*/
public $function;
- /**
- * @var int
- */
- public $places;
+ public int $places = 0;
/**
* CustomFunction constructor.
- * @param string $name
- * @param callable $function
* @param int $places
* @throws ReflectionException
* @throws IncorrectNumberOfFunctionParametersException
@@ -35,7 +27,8 @@ class CustomFunction
{
$this->name = $name;
$this->function = $function;
- if ($places === null) {
+
+ if (null === $places) {
$reflection = new ReflectionFunction($function);
$this->places = $reflection->getNumberOfParameters();
} else {
@@ -48,17 +41,18 @@ class CustomFunction
*
* @throws IncorrectNumberOfFunctionParametersException
*/
- public function execute(array &$stack): Token
+ public function execute(array &$stack) : Token
{
- if (count($stack) < $this->places) {
+ if (\count($stack) < $this->places) {
throw new IncorrectNumberOfFunctionParametersException($this->name);
}
$args = [];
+
for ($i = 0; $i < $this->places; $i++) {
- array_unshift($args, array_pop($stack)->value);
+ \array_unshift($args, \array_pop($stack)->value);
}
- $result = call_user_func_array($this->function, $args);
+ $result = \call_user_func_array($this->function, $args);
return new Token(Token::Literal, $result);
}
diff --git a/src/NXP/Classes/Operator.php b/src/NXP/Classes/Operator.php
index 1b1acc4..7dee06d 100644
--- a/src/NXP/Classes/Operator.php
+++ b/src/NXP/Classes/Operator.php
@@ -7,37 +7,21 @@ use ReflectionFunction;
class Operator
{
- /**
- * @var string
- */
- public $operator;
+ public string $operator = '';
- /**
- * @var bool
- */
- public $isRightAssoc;
+ public bool $isRightAssoc = false;
- /**
- * @var int
- */
- public $priority;
+ public int $priority = 0;
/**
* @var callable(\SplStack)
*/
public $function;
- /**
- * @var int
- */
- public $places;
+ public int $places = 0;
/**
* Operator constructor.
- * @param string $operator
- * @param bool $isRightAssoc
- * @param int $priority
- * @param callable $function
*/
public function __construct(string $operator, bool $isRightAssoc, int $priority, callable $function)
{
@@ -54,17 +38,18 @@ class Operator
*
* @throws IncorrectExpressionException
*/
- public function execute(array &$stack): Token
+ public function execute(array &$stack) : Token
{
- if (count($stack) < $this->places) {
+ if (\count($stack) < $this->places) {
throw new IncorrectExpressionException();
}
$args = [];
+
for ($i = 0; $i < $this->places; $i++) {
- array_unshift($args, array_pop($stack)->value);
+ \array_unshift($args, \array_pop($stack)->value);
}
- $result = call_user_func_array($this->function, $args);
+ $result = \call_user_func_array($this->function, $args);
return new Token(Token::Literal, $result);
}
diff --git a/src/NXP/Classes/Token.php b/src/NXP/Classes/Token.php
index 924771e..b2a0a64 100644
--- a/src/NXP/Classes/Token.php
+++ b/src/NXP/Classes/Token.php
@@ -4,31 +4,34 @@ namespace NXP\Classes;
class Token
{
- public const Literal = "literal";
- public const Variable = "variable";
- public const Operator = "operator";
- public const LeftParenthesis = "LP";
- public const RightParenthesis = "RP";
- public const Function = "function";
- public const ParamSeparator = "separator";
- public const String = "string";
- public const Space = "space";
-
- /** @var self::* */
- public $type = self::Literal;
-
- /** @var float|string */
+ public const Literal = 'literal';
+
+ public const Variable = 'variable';
+
+ public const Operator = 'operator';
+
+ public const LeftParenthesis = 'LP';
+
+ public const RightParenthesis = 'RP';
+
+ public const Function = 'function';
+
+ public const ParamSeparator = 'separator';
+
+ public const String = 'string';
+
+ public const Space = 'space';
+
+ public string $type = self::Literal;
+
public $value;
- /** @var string */
- public $name;
+ public ?string $name;
/**
* Token constructor.
- * @param self::* $type
- * @param float|string $value
*/
- public function __construct(string $type, $value, string $name = null)
+ public function __construct(string $type, $value, ?string $name = null)
{
$this->type = $type;
$this->value = $value;
diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php
index 087a78d..1bd6d6d 100644
--- a/src/NXP/Classes/Tokenizer.php
+++ b/src/NXP/Classes/Tokenizer.php
@@ -20,44 +20,24 @@ use SplStack;
*/
class Tokenizer
{
- /**
- * @var Token[]
- */
- public $tokens = [];
- /**
- * @var string
- */
- private $input = '';
- /**
- * @var string
- */
- private $numberBuffer = '';
- /**
- * @var string
- */
- private $stringBuffer = '';
- /**
- * @var bool
- */
- private $allowNegative = true;
- /**
- * @var Operator[]
- */
- private $operators = [];
+ public array $tokens = [];
- /**
- * @var bool
- */
- private $inSingleQuotedString = false;
+ private string $input = '';
- /**
- * @var bool
- */
- private $inDoubleQuotedString = false;
+ private string $numberBuffer = '';
+
+ private string $stringBuffer = '';
+
+ private bool $allowNegative = true;
+
+ private array $operators = [];
+
+ private bool $inSingleQuotedString = false;
+
+ private bool $inDoubleQuotedString = false;
/**
* Tokenizer constructor.
- * @param string $input
* @param Operator[] $operators
*/
public function __construct(string $input, array $operators)
@@ -66,109 +46,137 @@ class Tokenizer
$this->operators = $operators;
}
- public function tokenize(): self
+ public function tokenize() : self
{
- foreach (str_split($this->input, 1) as $ch) {
+ foreach (\str_split($this->input, 1) as $ch) {
switch (true) {
case $this->inSingleQuotedString:
- if ($ch === "'") {
+ if ("'" === $ch) {
$this->tokens[] = new Token(Token::String, $this->stringBuffer);
$this->inSingleQuotedString = false;
$this->stringBuffer = '';
+
continue 2;
}
$this->stringBuffer .= $ch;
+
continue 2;
+
case $this->inDoubleQuotedString:
- if ($ch === '"') {
+ if ('"' === $ch) {
$this->tokens[] = new Token(Token::String, $this->stringBuffer);
$this->inDoubleQuotedString = false;
$this->stringBuffer = '';
+
continue 2;
}
$this->stringBuffer .= $ch;
+
continue 2;
- case $ch == ' ' || $ch == "\n" || $ch == "\r" || $ch == "\t":
+
+ case ' ' == $ch || "\n" == $ch || "\r" == $ch || "\t" == $ch:
$this->tokens[] = new Token(Token::Space, '');
+
continue 2;
+
case $this->isNumber($ch):
- if ($this->stringBuffer != '') {
+ if ('' != $this->stringBuffer) {
$this->stringBuffer .= $ch;
+
continue 2;
}
$this->numberBuffer .= $ch;
$this->allowNegative = false;
+
break;
/** @noinspection PhpMissingBreakStatementInspection */
- case strtolower($ch) === 'e':
- if (strlen($this->numberBuffer) && strpos($this->numberBuffer, '.') !== false) {
+ case 'e' === \strtolower($ch):
+ if (\strlen($this->numberBuffer) && false !== \strpos($this->numberBuffer, '.')) {
$this->numberBuffer .= 'e';
$this->allowNegative = false;
+
break;
}
// no break
+ // Intentionally fall through
case $this->isAlpha($ch):
- if (strlen($this->numberBuffer)) {
+ if (\strlen($this->numberBuffer)) {
$this->emptyNumberBufferAsLiteral();
$this->tokens[] = new Token(Token::Operator, '*');
}
$this->allowNegative = false;
$this->stringBuffer .= $ch;
+
break;
- case $ch == '"':
+
+ case '"' == $ch:
$this->inDoubleQuotedString = true;
+
continue 2;
- case $ch == "'":
+
+ case "'" == $ch:
$this->inSingleQuotedString = true;
+
continue 2;
case $this->isDot($ch):
$this->numberBuffer .= $ch;
$this->allowNegative = false;
+
break;
+
case $this->isLP($ch):
- if ($this->stringBuffer != '') {
+ if ('' != $this->stringBuffer) {
$this->tokens[] = new Token(Token::Function, $this->stringBuffer);
$this->stringBuffer = '';
- } elseif (strlen($this->numberBuffer)) {
+ } elseif (\strlen($this->numberBuffer)) {
$this->emptyNumberBufferAsLiteral();
$this->tokens[] = new Token(Token::Operator, '*');
}
$this->allowNegative = true;
$this->tokens[] = new Token(Token::LeftParenthesis, '');
+
break;
+
case $this->isRP($ch):
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
$this->allowNegative = false;
$this->tokens[] = new Token(Token::RightParenthesis, '');
+
break;
+
case $this->isComma($ch):
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
$this->allowNegative = true;
$this->tokens[] = new Token(Token::ParamSeparator, '');
+
break;
+
default:
// special case for unary operations
- if ($ch == '-' || $ch == '+') {
+ if ('-' == $ch || '+' == $ch) {
if ($this->allowNegative) {
$this->allowNegative = false;
- $this->tokens[] = new Token(Token::Operator, $ch == '-' ? 'uNeg' : 'uPos');
+ $this->tokens[] = new Token(Token::Operator, '-' == $ch ? 'uNeg' : 'uPos');
+
continue 2;
}
// could be in exponent, in which case negative should be added to the numberBuffer
- if ($this->numberBuffer && $this->numberBuffer[strlen($this->numberBuffer) - 1] == 'e') {
+ if ($this->numberBuffer && 'e' == $this->numberBuffer[\strlen($this->numberBuffer) - 1]) {
$this->numberBuffer .= $ch;
+
continue 2;
}
}
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
- if ($ch != '$') {
- if (count($this->tokens) > 0) {
- if ($this->tokens[count($this->tokens) - 1]->type === Token::Operator) {
- $this->tokens[count($this->tokens) - 1]->value .= $ch;
+
+ if ('$' != $ch) {
+ if (\count($this->tokens) > 0) {
+ if (Token::Operator === $this->tokens[\count($this->tokens) - 1]->type) {
+ $this->tokens[\count($this->tokens) - 1]->value .= $ch;
} else {
$this->tokens[] = new Token(Token::Operator, $ch);
}
@@ -181,107 +189,76 @@ class Tokenizer
}
$this->emptyNumberBufferAsLiteral();
$this->emptyStrBufferAsVariable();
- return $this;
- }
-
- private function isNumber(string $ch): bool
- {
- return $ch >= '0' && $ch <= '9';
- }
-
- private function isAlpha(string $ch): bool
- {
- return $ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || $ch == '_';
- }
-
- private function emptyNumberBufferAsLiteral(): void
- {
- if (strlen($this->numberBuffer)) {
- $this->tokens[] = new Token(Token::Literal, $this->numberBuffer);
- $this->numberBuffer = '';
- }
- }
-
- private function isDot(string $ch): bool
- {
- return $ch == '.';
- }
-
- private function isLP(string $ch): bool
- {
- return $ch == '(';
- }
-
- private function isRP(string $ch): bool
- {
- return $ch == ')';
- }
- private function emptyStrBufferAsVariable(): void
- {
- if ($this->stringBuffer != '') {
- $this->tokens[] = new Token(Token::Variable, $this->stringBuffer);
- $this->stringBuffer = '';
- }
- }
-
- private function isComma(string $ch): bool
- {
- return $ch == ',';
+ return $this;
}
/**
- * @return Token[] Array of tokens in revers polish notation
* @throws IncorrectBracketsException
* @throws UnknownOperatorException
+ * @return Token[] Array of tokens in revers polish notation
*/
- public function buildReversePolishNotation(): array
+ public function buildReversePolishNotation() : array
{
$tokens = [];
/** @var SplStack<Token> $stack */
$stack = new SplStack();
+
foreach ($this->tokens as $token) {
switch ($token->type) {
case Token::Literal:
case Token::Variable:
case Token::String:
$tokens[] = $token;
+
break;
+
case Token::Function:
case Token::LeftParenthesis:
$stack->push($token);
+
break;
+
case Token::ParamSeparator:
- while ($stack->top()->type !== Token::LeftParenthesis) {
- if ($stack->count() === 0) {
+ while (Token::LeftParenthesis !== $stack->top()->type) {
+ if (0 === $stack->count()) {
throw new IncorrectBracketsException();
}
$tokens[] = $stack->pop();
}
+
break;
+
case Token::Operator:
- if (!array_key_exists($token->value, $this->operators)) {
+ if (! \array_key_exists($token->value, $this->operators)) {
throw new UnknownOperatorException($token->value);
}
$op1 = $this->operators[$token->value];
- while ($stack->count() > 0 && $stack->top()->type === Token::Operator) {
- if (!array_key_exists($stack->top()->value, $this->operators)) {
+
+ while ($stack->count() > 0 && Token::Operator === $stack->top()->type) {
+ if (! \array_key_exists($stack->top()->value, $this->operators)) {
throw new UnknownOperatorException($stack->top()->value);
}
$op2 = $this->operators[$stack->top()->value];
+
if ($op2->priority >= $op1->priority) {
$tokens[] = $stack->pop();
+
continue;
}
+
break;
}
$stack->push($token);
+
break;
+
case Token::RightParenthesis:
while (true) {
try {
$ctoken = $stack->pop();
- if ($ctoken->type === Token::LeftParenthesis) {
+
+ if (Token::LeftParenthesis === $ctoken->type) {
break;
}
$tokens[] = $ctoken;
@@ -289,24 +266,77 @@ class Tokenizer
throw new IncorrectBracketsException();
}
}
- if ($stack->count() > 0 && $stack->top()->type == Token::Function) {
+
+ if ($stack->count() > 0 && Token::Function == $stack->top()->type) {
$tokens[] = $stack->pop();
}
+
break;
+
case Token::Space:
//do nothing
}
}
- while ($stack->count() !== 0) {
- if ($stack->top()->type === Token::LeftParenthesis || $stack->top()->type === Token::RightParenthesis) {
+
+ while (0 !== $stack->count()) {
+ if (Token::LeftParenthesis === $stack->top()->type || Token::RightParenthesis === $stack->top()->type) {
throw new IncorrectBracketsException();
}
- if ($stack->top()->type === Token::Space) {
+
+ if (Token::Space === $stack->top()->type) {
$stack->pop();
+
continue;
}
$tokens[] = $stack->pop();
}
+
return $tokens;
}
+
+ private function isNumber(string $ch) : bool
+ {
+ return $ch >= '0' && $ch <= '9';
+ }
+
+ private function isAlpha(string $ch) : bool
+ {
+ return $ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || '_' == $ch;
+ }
+
+ private function emptyNumberBufferAsLiteral() : void
+ {
+ if (\strlen($this->numberBuffer)) {
+ $this->tokens[] = new Token(Token::Literal, $this->numberBuffer);
+ $this->numberBuffer = '';
+ }
+ }
+
+ private function isDot(string $ch) : bool
+ {
+ return '.' == $ch;
+ }
+
+ private function isLP(string $ch) : bool
+ {
+ return '(' == $ch;
+ }
+
+ private function isRP(string $ch) : bool
+ {
+ return ')' == $ch;
+ }
+
+ private function emptyStrBufferAsVariable() : void
+ {
+ if ('' != $this->stringBuffer) {
+ $this->tokens[] = new Token(Token::Variable, $this->stringBuffer);
+ $this->stringBuffer = '';
+ }
+ }
+
+ private function isComma(string $ch) : bool
+ {
+ return ',' == $ch;
+ }
}