diff options
Diffstat (limited to 'src/NXP/Classes')
-rw-r--r-- | src/NXP/Classes/Calculator.php | 2 | ||||
-rw-r--r-- | src/NXP/Classes/CustomFunction.php | 36 | ||||
-rw-r--r-- | src/NXP/Classes/Token.php | 2 | ||||
-rw-r--r-- | src/NXP/Classes/Tokenizer.php | 34 |
4 files changed, 53 insertions, 21 deletions
diff --git a/src/NXP/Classes/Calculator.php b/src/NXP/Classes/Calculator.php index 10adc24..6b63503 100644 --- a/src/NXP/Classes/Calculator.php +++ b/src/NXP/Classes/Calculator.php @@ -74,7 +74,7 @@ class Calculator if (! \array_key_exists($token->value, $this->functions)) { throw new UnknownFunctionException($token->value); } - $stack[] = $this->functions[$token->value]->execute($stack); + $stack[] = $this->functions[$token->value]->execute($stack, $token->paramCount); } elseif (Token::Operator === $token->type) { if (! \array_key_exists($token->value, $this->operators)) { throw new UnknownOperatorException($token->value); diff --git a/src/NXP/Classes/CustomFunction.php b/src/NXP/Classes/CustomFunction.php index 843cf82..1ebdd2c 100644 --- a/src/NXP/Classes/CustomFunction.php +++ b/src/NXP/Classes/CustomFunction.php @@ -2,6 +2,7 @@ namespace NXP\Classes; +use NXP\Exception\IncorrectFunctionParameterException; use NXP\Exception\IncorrectNumberOfFunctionParametersException; use ReflectionException; use ReflectionFunction; @@ -15,41 +16,48 @@ class CustomFunction */ public $function; - public int $places = 0; + private ReflectionFunction $reflectionFunction; /** * CustomFunction constructor. * * @throws ReflectionException - * @throws IncorrectNumberOfFunctionParametersException */ - public function __construct(string $name, callable $function, ?int $places = null) + public function __construct(string $name, callable $function) { $this->name = $name; $this->function = $function; + $this->reflectionFunction = new ReflectionFunction($function); - if (null === $places) { - $reflection = new ReflectionFunction($function); - $this->places = $reflection->getNumberOfParameters(); - } else { - $this->places = $places; - } } /** * @param array<Token> $stack * - * @throws IncorrectNumberOfFunctionParametersException + * @throws IncorrectNumberOfFunctionParametersException|IncorrectFunctionParameterException */ - public function execute(array &$stack) : Token + public function execute(array &$stack, int $paramCountInStack) : Token { - if (\count($stack) < $this->places) { + if ($paramCountInStack < $this->reflectionFunction->getNumberOfRequiredParameters()) { throw new IncorrectNumberOfFunctionParametersException($this->name); } $args = []; - for ($i = 0; $i < $this->places; $i++) { - \array_unshift($args, \array_pop($stack)->value); + if ($paramCountInStack > 0) { + $reflectionParameters = $this->reflectionFunction->getParameters(); + + for ($i = 0; $i < $paramCountInStack; $i++) { + $value = \array_pop($stack)->value; + $valueType = \gettype($value); + $reflectionParameter = $reflectionParameters[\min(\count($reflectionParameters) - 1, $i)]; + //TODO to support type check for union types (php >= 8.0) and intersection types (php >= 8.1), we should increase min php level in composer.json + // For now, only support basic types. @see testFunctionParameterTypes + if ($reflectionParameter->hasType() && $reflectionParameter->getType()->getName() !== $valueType){ + throw new IncorrectFunctionParameterException(); + } + + \array_unshift($args, $value); + } } $result = \call_user_func_array($this->function, $args); diff --git a/src/NXP/Classes/Token.php b/src/NXP/Classes/Token.php index e75b17e..7532e77 100644 --- a/src/NXP/Classes/Token.php +++ b/src/NXP/Classes/Token.php @@ -28,6 +28,8 @@ class Token public ?string $name; + public ?int $paramCount = null;//to store function parameter count in stack + /** * Token constructor. * diff --git a/src/NXP/Classes/Tokenizer.php b/src/NXP/Classes/Tokenizer.php index 32404a2..23e1cc9 100644 --- a/src/NXP/Classes/Tokenizer.php +++ b/src/NXP/Classes/Tokenizer.php @@ -20,7 +20,7 @@ use SplStack; */ class Tokenizer { - /** @var array<Token> */ + /** @var array<Token> */ public array $tokens = []; private string $input = ''; @@ -31,7 +31,7 @@ class Tokenizer private bool $allowNegative = true; - /** @var array<Operator> */ + /** @var array<Operator> */ private array $operators = []; private bool $inSingleQuotedString = false; @@ -99,8 +99,8 @@ class Tokenizer break; } - // no break - // Intentionally fall through + // no break + // Intentionally fall through case $this->isAlpha($ch): if (\strlen($this->numberBuffer)) { $this->emptyNumberBufferAsLiteral(); @@ -196,8 +196,8 @@ class Tokenizer } /** - * @throws IncorrectBracketsException * @throws UnknownOperatorException + * @throws IncorrectBracketsException * @return Token[] Array of tokens in revers polish notation */ public function buildReversePolishNotation() : array @@ -205,6 +205,10 @@ class Tokenizer $tokens = []; /** @var SplStack<Token> $stack */ $stack = new SplStack(); + /** + * @var SplStack<int> $paramCounter + */ + $paramCounter = new SplStack(); foreach ($this->tokens as $token) { switch ($token->type) { @@ -213,9 +217,21 @@ class Tokenizer case Token::String: $tokens[] = $token; + if ($paramCounter->count() > 0 && 0 === $paramCounter->top()) { + $paramCounter->push($paramCounter->pop() + 1); + } + break; case Token::Function: + if ($paramCounter->count() > 0 && 0 === $paramCounter->top()) { + $paramCounter->push($paramCounter->pop() + 1); + } + $stack->push($token); + $paramCounter->push(0); + + break; + case Token::LeftParenthesis: $stack->push($token); @@ -228,6 +244,7 @@ class Tokenizer } $tokens[] = $stack->pop(); } + $paramCounter->push($paramCounter->pop() + 1); break; @@ -270,7 +287,12 @@ class Tokenizer } if ($stack->count() > 0 && Token::Function == $stack->top()->type) { - $tokens[] = $stack->pop(); + /** + * @var Token $f + */ + $f = $stack->pop(); + $f->paramCount = $paramCounter->pop(); + $tokens[] = $f; } break; |