Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- function calc($s)
- {
- $s = '(' . $s . ')';
- $Operands = array();
- $Functions = array();
- $pos = 0;
- $prevToken = 'x';
- while (($token = getToken($s, $pos)) !== null) {
- // разруливаем унарный + и -
- if (!ctype_digit($token[0]) && !ctype_digit($prevToken[0]) &&
- // если эту сточку заменить на (char)prevToken != ')' &&
- // то можно будет писать (2 * -5) или даже (2 - -4)
- // но нужно будет ввести ещё одну проверку так, как запись 2 + -+-+-+2 тоже будет работать :)
- $prevToken[0] === '(' &&
- ($token[0] === '+' || $token[0] === '-'))
- {
- $Operands[] = 0; // Добавляем нулевой элемент
- }
- if (is_numeric($token)) // Если операнд
- {
- $Operands[] = $token; // то просто кидаем в стек
- }
- // в данном случае у нас только числа и операции. но можно добавить функции, переменные и т.д. и т.п.
- else if (!is_numeric($token[0])) // Если операция
- {
- if ($token[0] === ')')
- {
- // Скобка - исключение из правил. выталкивает все операции до первой открывающейся
- while (count($Functions) > 0 && end($Functions) !== '(')
- {
- popFunction($Operands, $Functions);
- }
- array_pop($Functions); // Удаляем саму скобку "("
- }
- else
- {
- while (canPop($token[0], $Functions)) // Если можно вытолкнуть
- {
- popFunction($Operands, $Functions); // то выталкиваем
- }
- $Functions[] = $token[0]; // Кидаем новую операцию в стек
- }
- }
- $prevToken = $token;
- }
- if (count($Operands) > 1 || count($Functions) > 0)
- {
- throw new Exception('Ошибка в выражении');
- }
- return array_pop($Operands);
- }
- function popFunction(& $Operands, & $Functions)
- {
- $B = (float) array_pop($Operands);
- $A = (float) array_pop($Operands);
- switch (array_pop($Functions))
- {
- case '+': $Operands[] = $A + $B;
- break;
- case '-': $Operands[] = $A - $B;
- break;
- case '*': $Operands[] = $A * $B;
- break;
- case '/': $Operands[] = $A / $B;
- break;
- }
- }
- function canPop($op1, & $Functions)
- {
- if (!count($Functions))
- {
- return false;
- }
- $p1 = (int) getPriority($op1);
- $p2 = (int) getPriority(end($Functions));
- return $p1 >= 0 && $p2 >= 0 && $p1 >= $p2;
- }
- function getPriority($op)
- {
- switch ($op[0])
- {
- case '(':
- return -1; // не выталкивает сам и не дает вытолкнуть себя другим
- case '*':
- case '/':
- return 1;
- case '+':
- case '-':
- return 2;
- default:
- throw new Exception("недопустимая операция");
- }
- }
- function getToken($s, & $pos)
- {
- readWhiteSpace($s, $pos);
- if ($pos == strlen($s)) // конец строки
- {
- return null;
- }
- if (is_numeric($s[$pos]))
- {
- return readDouble($s, $pos);
- }
- else
- {
- return readFunction($s, $pos);
- }
- }
- function readFunction($s, & $pos)
- {
- // в данном случае все операции состоят из одного символа
- // но мы можем усложнить код добавив == && || mod div и ещё чегонить
- return (string) $s[$pos++];
- }
- function readDouble($s, & $pos)
- {
- $res = '';
- while ($pos < strlen($s) && (ctype_digit($s[$pos]) || $s[$pos] == '.'))
- {
- $res .= $s[$pos++];
- }
- return $res;
- }
- // Считывает все проблемы и прочие левые символы.
- function readWhiteSpace($s, & $pos)
- {
- while ($pos < strlen($s) && ctype_space($s[$pos]))
- {
- $pos++;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment