1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Calc.php |
5
|
|
|
* |
6
|
|
|
* @date 28.03.2015 0:54:24 |
7
|
|
|
* @copyright Sklyarov Alexey <[email protected]> |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Sufir\Calc; |
11
|
|
|
|
12
|
|
|
use Sufir\Calc\Token\AbstractToken; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Calc |
16
|
|
|
* |
17
|
|
|
* Description of Calc |
18
|
|
|
* |
19
|
|
|
* @author Sklyarov Alexey <[email protected]> |
20
|
|
|
* @package Sufir\Calc |
21
|
|
|
*/ |
22
|
|
|
class Calc |
23
|
|
|
{ |
24
|
|
|
protected $functions = []; |
25
|
|
|
|
26
|
|
|
protected $variables = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* |
30
|
|
|
* @param \Sufir\Calc\Token\AbstractToken[] $tokens |
31
|
|
|
*/ |
32
|
|
|
public function evaluate(array $tokens) |
33
|
|
|
{ |
34
|
|
|
$stack = new \SplStack; |
35
|
|
|
foreach ($tokens as $token) { |
36
|
|
|
// Если на вход подан операнд, он помещается на вершину стека |
37
|
|
|
if ($token->isNumber()) { |
38
|
|
|
$stack->push($token); |
39
|
|
|
|
40
|
|
|
// Если на вход подан знак операции |
41
|
|
|
} elseif ($token->isOperator()) { |
42
|
|
|
if ($stack->count() < 2) { |
43
|
|
|
throw new \Exception('Ошибочка вышла! Недостаточно операндов для бинарного оператора: ' . $token); |
44
|
|
|
|
45
|
|
|
// операция выполняется над требуемым количеством значений, |
46
|
|
|
// извлечённых из стека, взятых в порядке добавления |
47
|
|
|
} elseif ($stack->count() > 1) { |
48
|
|
|
$second = $stack->pop(); |
49
|
|
|
$first = $stack->pop(); |
50
|
|
|
|
51
|
|
|
$result = $this->calc((string) $token, (string) $first, (string) $second); |
52
|
|
|
|
53
|
|
|
// Результат выполненной операции кладётся на вершину стека |
54
|
|
|
$stack->push($result); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
// Если на вход подана функция |
58
|
|
|
} elseif ($token->isFunction()) { |
59
|
|
|
if (!isset($this->functions[$token->getValue()])) { |
60
|
|
|
throw new \Exception('Не найдена функция: ' . $token->getValue()); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
$countOfArguments = $this->functions[$token->getValue()]['args']; |
64
|
|
|
|
65
|
|
|
if ($stack->count() < $countOfArguments) { |
66
|
|
|
throw new \Exception('Ошибочка вышла! Недостаточно аргументов для функции: ' . $token); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
$arguments = array(); |
70
|
|
|
|
71
|
|
|
for ($idx = 0; $idx < $countOfArguments; $idx++) { |
72
|
|
|
$arguments[] = ((string) $stack->pop()) - 0; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
$arguments = array_reverse($arguments); |
76
|
|
|
|
77
|
|
|
$result = call_user_func_array($this->functions[$token->getValue()]['func'], $arguments); |
78
|
|
|
|
79
|
|
|
$stack->push($result); |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
return $stack->top(); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* |
88
|
|
|
* @param string $name |
89
|
|
|
* @param \Closure $callable |
90
|
|
|
* @return \Sufir\Calc\Calc |
91
|
|
|
*/ |
92
|
|
View Code Duplication |
public function registerFunction($name, \Closure $callable) |
|
|
|
|
93
|
|
|
{ |
94
|
|
|
$objReflector = new \ReflectionObject($callable); |
95
|
|
|
$reflector = $objReflector->getMethod('__invoke'); |
96
|
|
|
$parameters = $reflector->getParameters(); |
97
|
|
|
|
98
|
|
|
$this->functions[$name] = array( |
99
|
|
|
'args' => count($parameters), |
100
|
|
|
'func' => $callable, |
101
|
|
|
); |
102
|
|
|
|
103
|
|
|
return $this; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* |
108
|
|
|
* @param string $name |
109
|
|
|
* @param \Closure $callable |
|
|
|
|
110
|
|
|
* @return \Sufir\Calc\Calc |
111
|
|
|
*/ |
112
|
|
View Code Duplication |
public function registerVariable($name, $value) |
|
|
|
|
113
|
|
|
{ |
114
|
|
|
$objReflector = new \ReflectionObject($callable); |
|
|
|
|
115
|
|
|
$reflector = $objReflector->getMethod('__invoke'); |
116
|
|
|
$parameters = $reflector->getParameters(); |
117
|
|
|
|
118
|
|
|
$this->functions[$name] = array( |
119
|
|
|
'args' => count($parameters), |
120
|
|
|
'func' => $callable, |
121
|
|
|
); |
122
|
|
|
|
123
|
|
|
return $this; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @param string $operator |
128
|
|
|
* @param integer|float $firstOperand |
129
|
|
|
* @param integer|float $secondOperand |
130
|
|
|
* @return int |
131
|
|
|
*/ |
132
|
|
|
protected function calc($operator, $firstOperand, $secondOperand) |
|
|
|
|
133
|
|
|
{ |
134
|
|
|
switch ($operator) { |
135
|
|
|
case '^': |
136
|
|
|
return pow($firstOperand, $secondOperand); |
137
|
|
|
case '*': |
138
|
|
|
return $firstOperand * $secondOperand; |
139
|
|
|
case '/': |
140
|
|
|
return $firstOperand / $secondOperand; |
141
|
|
|
case '+': |
142
|
|
|
return $firstOperand + $secondOperand; |
143
|
|
|
case '-': |
144
|
|
|
return $firstOperand - $secondOperand; |
145
|
|
|
default: |
146
|
|
|
return 0; |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.