Calculator   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
eloc 37
c 1
b 0
f 0
dl 0
loc 87
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A getOperators() 0 3 1
C calculate() 0 51 12
A getFunctions() 0 3 1
1
<?php
2
3
/**
4
 * Platine Expression
5
 *
6
 * Platine Expression is an expression parser, evaluator with support of custom
7
 * operators and functions
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Expression
12
 * Copyright (c) Alexander Kiryukhin
13
 *
14
 * Permission is hereby granted, free of charge, to any person obtaining a copy
15
 * of this software and associated documentation files (the "Software"), to deal
16
 * in the Software without restriction, including without limitation the rights
17
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
 * copies of the Software, and to permit persons to whom the Software is
19
 * furnished to do so, subject to the following conditions:
20
 *
21
 * The above copyright notice and this permission notice shall be included in all
22
 * copies or substantial portions of the Software.
23
 *
24
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
 * SOFTWARE.
31
 */
32
33
/**
34
 * @file Calculator.php
35
 *
36
 * The Calculator class
37
 *
38
 *  @package    Platine\Expression
39
 *  @author Platine Developers Team
40
 *  @copyright  Copyright (c) 2020
41
 *  @license    http://opensource.org/licenses/MIT  MIT License
42
 *  @link   https://www.platine-php.com
43
 *  @version 1.0.0
44
 *  @filesource
45
 */
46
declare(strict_types=1);
47
48
namespace Platine\Expression;
49
50
use Platine\Expression\Exception\IncorrectExpressionException;
51
use Platine\Expression\Exception\UnknownFunctionException;
52
use Platine\Expression\Exception\UnknownOperatorException;
53
use Platine\Expression\Exception\UnknownVariableException;
54
use Platine\Expression\Token;
55
56
/**
57
 * @class Calculator
58
 * @package Platine\Expression
59
 */
60
class Calculator
61
{
62
    /**
63
     * Create new instance
64
     * @param array<string, CustomFunction> $functions
65
     * @param array<string, Operator> $operators
66
     */
67
    public function __construct(protected array $functions, protected array $operators)
68
    {
69
    }
70
71
    /**
72
     * Calculate array of tokens in reverse polish notation
73
     * @param Token[] $tokens
74
     * @param array<string, float|string> $variables
75
     * @param callable|null $variableNotFoundHandler
76
     * @return mixed
77
     */
78
    public function calculate(
79
        array $tokens,
80
        array $variables,
81
        ?callable $variableNotFoundHandler = null
82
    ): mixed {
83
        /** @var Token[] $stack */
84
        $stack = [];
85
        foreach ($tokens as $token) {
86
            if (in_array($token->getType(), [Token::LITERAL, Token::STRING])) {
87
                $stack[] = $token;
88
            } elseif (Token::VARIABLE === $token->getType()) {
89
                $variable = $token->getValue();
90
                $value = null;
91
                if (array_key_exists($variable, $variables)) {
92
                    $value = $variables[$variable];
93
                } elseif ($variableNotFoundHandler !== null) {
94
                    $value = call_user_func($variableNotFoundHandler, $variable);
95
                } else {
96
                    throw new UnknownVariableException(sprintf(
97
                        'Unknown variable [%s]',
98
                        $variable
99
                    ));
100
                }
101
                $stack[] = new Token(Token::LITERAL, $value, $variable);
102
            } elseif (Token::FUNCTION === $token->getType()) {
103
                if (! array_key_exists($token->getValue(), $this->functions)) {
104
                    throw new UnknownFunctionException(sprintf(
105
                        'Unknown function [%s]',
106
                        $token->getValue()
107
                    ));
108
                }
109
                $stack[] = $this->functions[$token->getValue()]->execute(
110
                    $stack,
111
                    $token->getParamCount()
112
                );
113
            } elseif (Token::OPERATOR === $token->getType()) {
114
                if (! array_key_exists($token->getValue(), $this->operators)) {
115
                    throw new UnknownOperatorException(sprintf(
116
                        'Unknown operator [%s]',
117
                        $token->getValue()
118
                    ));
119
                }
120
                $stack[] = $this->operators[$token->getValue()]->execute($stack);
121
            }
122
        }
123
        $result = array_pop($stack);
124
        if ($result === null || count($stack) > 0) {
125
            throw new IncorrectExpressionException('Expression stack is not empty');
126
        }
127
128
        return $result->getValue();
129
    }
130
131
    /**
132
     * Return the list of functions
133
     * @return array<string, CustomFunction>
134
     */
135
    public function getFunctions(): array
136
    {
137
        return $this->functions;
138
    }
139
140
    /**
141
     * Return the list of operators
142
     * @return array<string, Operator>
143
     */
144
    public function getOperators(): array
145
    {
146
        return $this->operators;
147
    }
148
}
149