Last active
March 9, 2018 21:04
-
-
Save eonarik/a85f76c40ed4a6fda8e379eef14b6e2f to your computer and use it in GitHub Desktop.
modx snippet math
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// v.1.1.0 | |
/** | |
* Модификатор для несложных математических вычислений (знаки /+-*). | |
* Примеры использования: | |
* прибавим к этому значение 0.5 и получим [[+value:math=`+0.5`]] | |
* (2+2)*2 равно [[math?input=`(2+2)*2`]] | |
**/ | |
if(!$input) return; | |
if (!function_exists('calculate')) { | |
// Исключения для парсера выражений | |
class AriphmeticException extends Exception { | |
function __construct($msg, $code) { | |
return parent::__construct($msg, $code); | |
} | |
function __toString() { | |
return get_class($this) . '(' | |
. $this->code . '): ' | |
. $this->message; | |
} | |
} | |
// Собственно сам вычислитель выражений | |
function calculate($statement) { | |
if (!is_string($statement)) { | |
throw new AriphmeticException('Wrong type', 1); | |
} | |
$calcQueue = array(); | |
$operStack = array(); | |
$operPriority = array( | |
'(' => 0, | |
')' => 0, | |
'+' => 1, | |
'-' => 1, | |
'%' => 2, | |
'*' => 2, | |
'/' => 2, | |
); | |
$token = ''; | |
foreach (str_split($statement) as $char) { | |
// Если цифра, то собираем из цифр число | |
if ($char >= '0' && $char <= '9') { | |
$token .= $char; | |
} else { | |
// Если число накопилось, сохраняем в очереди вычисления | |
if (strlen($token)) { | |
array_push($calcQueue, $token); | |
$token = ''; | |
} | |
// Если найденный символ - операция (он есть в списке приоритетов) | |
if (isset($operPriority[$char])) { | |
if (')' == $char) { | |
// Если символ - закрывающая скобка, переносим операции из стека в очередь вычисления пока не встретим открывающую скобку | |
while (!empty($operStack)) { | |
$oper = array_pop($operStack); | |
if ('(' == $oper) { | |
break; | |
} | |
array_push($calcQueue, $oper); | |
} | |
if ('(' != $oper) { | |
// Упс! А открывающей-то не было. Сильно ругаемся (18+) | |
throw new AriphmeticException('Unexpected ")"', 2); | |
} | |
} else { | |
// Встретили операцию кроме скобки. Переносим операции с меньшим приоритетом в очередь вычисления | |
while (!empty($operStack) && '(' != $char) { | |
$oper = array_pop($operStack); | |
if ($operPriority[$char] > $operPriority[$oper]) { | |
array_push($operStack, $oper); | |
break; | |
} | |
if ('(' != $oper) { | |
array_push($calcQueue, $oper); | |
} | |
} | |
// Кладем операцию на стек операций | |
array_push($operStack, $char); | |
} | |
} elseif (strpos(' ', $char) !== FALSE) { | |
// Игнорируем пробелы (можно добавить что еще игнорируем) | |
} else { | |
// Встретили что-то непонятное (мы так не договаривались). Опять ругаемся | |
throw new AriphmeticException('Unexpected symbol "' . $char . '"', 3); | |
} | |
} | |
} | |
// Вроде все разобрали, но если остались циферки добавляем их в очередь вычисления | |
if (strlen($token)) { | |
array_push($calcQueue, $token); | |
$token = ''; | |
} | |
// ... и оставшиеся в стеке операции | |
if (!empty($operStack)) { | |
while ($oper = array_pop($operStack)) { | |
if ('(' == $oper) { | |
// ... кроме открывающих скобок. Это верный признак отсутствующей закрывающей | |
throw new AriphmeticException('Unexpected "("', 4); | |
} | |
array_push($calcQueue, $oper); | |
} | |
} | |
$calcStack = array(); | |
// Теперь вычисляем все то, что напарсили | |
// Тут ошибки не ловил, но они могут быть (это домашнее задание) | |
foreach ($calcQueue as $token) { | |
switch ($token) { | |
case '+': | |
$arg2 = array_pop($calcStack); | |
$arg1 = array_pop($calcStack); | |
array_push($calcStack, $arg1 + $arg2); | |
break; | |
case '-': | |
$arg2 = array_pop($calcStack); | |
$arg1 = array_pop($calcStack); | |
array_push($calcStack, $arg1 - $arg2); | |
break; | |
case '*': | |
$arg2 = array_pop($calcStack); | |
$arg1 = array_pop($calcStack); | |
array_push($calcStack, $arg1 * $arg2); | |
break; | |
case '/': | |
$arg2 = array_pop($calcStack); | |
$arg1 = array_pop($calcStack); | |
array_push($calcStack, $arg1 / $arg2); | |
break; | |
case '%': | |
$arg2 = array_pop($calcStack); | |
$arg1 = array_pop($calcStack); | |
array_push($calcStack, $arg1 % $arg2); | |
break; | |
default: | |
array_push($calcStack, $token); | |
} | |
} | |
return array_pop($calcStack); | |
} | |
} | |
return calculate($input . $options); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment