Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- class Calculator
- {
- const Error_Parse = 'parse_error';
- const Error_Calc = 'calc_error';
- public $owner;
- public function __construct($owner)
- {
- $this->owner = $owner;
- }
- /**
- * Пользовательские функции
- * @virtual
- * @param string $value
- * @return mixed
- */
- public function cb_var($value)
- {
- return '';
- }
- /**
- * Пользовательские функции
- * @virtual
- * @param string $name
- * @param array $args
- * @return mixed
- */
- public function cb_func($name, $args)
- {
- switch ($name)
- {
- case 'replace':
- return str_replace($args[0], $args[1], $args[2]);
- default:
- return '';
- }
- }
- private function parse($expression, &$APos, $AValue)
- {
- //если ещё не начинали вычислений, то первое слагаемое ещё не определено
- if ($AValue === null)
- //получаем первое "слагаемое"
- if (($AValue = $this->token($expression, $APos)) === self::Error_Parse)
- return self::Error_Parse;
- //получаем оператор (или, в случае если не поставили "*" между множителями, то второй множитель)
- if (($Symbol = $this->symbol($expression, $APos)) === self::Error_Parse)
- //если не удалось получить, возвращаем извесное слагаемое
- return $this->value($AValue);
- //пытаемся получить второе слагаемое
- if (($Value = $this->token($expression, $APos)) === self::Error_Parse)
- //если не удалось его получить, то не будем сообщать об ошибке
- //в выражении, а заполним переменную пустой строкой.
- $Value = "''";
- //если парсим тернарное выражение
- elseif ($Symbol === '?')
- {
- //получаем обе резулитирующие части тернарного выр.
- $v = $this->token($expression, $APos);
- //и в зависимости от результата сравнения оставляем одну из
- $Value = ($AValue ? $Value : $v);
- }
- if (in_array($Symbol, array('*', '/'), true))
- $arr = array('^');
- elseif (in_array($Symbol, array('+', '-'), true))
- $arr = array('^', '*', '/');
- elseif (in_array($Symbol, array('=', '!=', '<', '>'), true))
- $arr = array('^', '*', '/', '+', '-');
- else
- $arr = false;
- if ($arr)
- {
- $pos = $APos;
- while ($APos < strlen($expression))
- {
- if (($v = $this->symbol($expression, $pos)) === self::Error_Parse)
- break;
- elseif (in_array($v, $arr, true))
- {
- if (($v = $this->parse($expression, $APos, $Value)) === self::Error_Parse)
- break;
- $Value = $v;
- $pos = $APos;
- }
- else
- break;
- }
- }
- $AValue = $this->value($AValue);
- $Value = $this->value($Value);
- switch ($Symbol)
- {
- case '^':
- return $this->pow($AValue, $Value);
- case '*':
- return $this->umn($AValue, $Value);
- case '/':
- return $this->del($AValue, $Value);
- case '+':
- return $this->plus($AValue, $Value);
- case '-':
- return $this->minus($AValue, $Value);
- case '?':
- return $Value;
- case '=':
- case '!=':
- case '<':
- case '>':
- return $this->compare($Symbol, $AValue, $Value);
- default:
- return self::Error_Parse;
- }
- }
- private function symbol($expression, &$pos)
- {
- $length = strlen($expression);
- while (true)
- {
- if ($pos >= $length) return self::Error_Parse;
- elseif ($expression[$pos] === ' ') $pos++;
- else break;
- }
- switch ($expression[$pos])
- {
- case '^':
- case '*':
- case '/':
- case '+':
- case '-':
- case '=':
- case '<':
- case '>':
- $len = 1;
- break;
- case '!':
- $len = 2;
- break;
- case '?':
- return '?';
- default:
- //если это ни один из известных символов, значит это знак "*",
- //который не поставили между множителями.
- return '*';
- }
- $pos += $len;
- return substr($expression, $pos - $len, $len);
- }
- private function token($expression, &$pos)
- {
- for ($length = strlen($expression); true;)
- {
- if ($pos >= $length) return -1;
- if ($expression[$pos] === ' ') $pos++;
- else break;
- }
- $from = $pos;
- $len = $this->token_len($expression, $pos);
- if ($len === -1)
- return self::Error_Parse;
- $pos += $len;
- return substr($expression, $from, $pos - $from);
- }
- private function token_len($expression, $pos)
- {
- $length = strlen($expression);
- if ($pos >= $length) return -1;
- $nums = array('.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
- if (in_array($expression[$pos], $nums, true))
- {
- $from = $pos;
- while (($pos < $length) && in_array($expression[$pos], $nums, true))
- $pos++;
- return $pos - $from;
- }
- else
- switch ($expression[$pos])
- {
- case '[':
- $from = $pos;
- while ($pos < $length)
- {
- $pos++;
- if ($expression[$pos] === ']') break;
- }
- $pos++;
- return $pos - $from;
- case '`': //устаревшее
- $from = $pos;
- while ($pos < $length)
- {
- $pos++;
- if ($expression[$pos] === '`') break;
- }
- $pos++;
- return $pos - $from;
- case '@':
- $from = $pos;
- $sc = -1;
- while ($pos < $length)
- {
- if ($expression[$pos] === '(') $sc++;
- elseif ($expression[$pos] === ')') $sc--;
- $pos++;
- if (($sc === 0) && ($expression[$pos] === ')')) break;
- }
- $pos++;
- return $pos - $from;
- case '?':
- $from = $pos;
- $sc = 0;
- while ($pos < $length)
- {
- if ($expression[$pos] === '(') $sc++;
- elseif ($expression[$pos] === ')') $sc--;
- $pos++;
- if (($sc === 0) && ($expression[$pos] === ':')) break;
- }
- return $pos - $from;
- case ':':
- return $length - $pos;
- case '(':
- $from = $pos;
- $sc = -1;
- while ($pos < $length)
- {
- if ($expression[$pos] === '(') $sc++;
- elseif ($expression[$pos] === ')') $sc--;
- $pos++;
- if (($sc === 0) && ($expression[$pos] === ')')) break;
- }
- $pos++;
- return $pos - $from;
- case '\'':
- $from = $pos;
- while ($pos < $length)
- {
- $pos++;
- while ($expression[$pos] === '\'')
- {
- if ($pos + 1 === $length) break;
- elseif ($expression[$pos + 1] === '\'')
- $pos++;
- else
- break;
- $pos++;
- }
- if ($expression[$pos] === '\'') break;
- }
- $pos++;
- return $pos - $from;
- default:
- return 0;
- }
- }
- private function value($value)
- {
- if (strlen($value) === 0) return "''";
- switch ($value[0])
- {
- case '@':
- return $this->funct($value);
- case '[':
- case '`':
- return $this->variable($value);
- case '(':
- return $this->subexp($value);
- case '?':
- case ':':
- return $this->ternval($value);
- default:
- return $value;
- }
- // return (is_numeric($value) ? $value : "'$value'");
- }
- private function variable($value)
- {
- $value = substr($value, 1, -1);
- $value = $this->cb_var($value);
- return (is_float($value) || is_int($value) ? $value : "'$value'");
- }
- private function funct($value)
- {
- $pos = 0;
- $args = array();
- $flength = strlen($value);
- while ($pos < $flength)
- {
- if ($value[$pos] === '(') break;
- $pos++;
- }
- $fname = substr($value, 1, $pos - 1);
- $pos++;
- $from = $pos;
- while ($pos < $flength)
- {
- switch ($value[$pos])
- {
- case '(':
- case '\'':
- case '@':
- case '[':
- case '`':
- $pos += $this->token_len($value, $pos);
- break;
- case ',':
- $args[] = substr($value, $from, $pos - $from);
- $pos++;
- $from = $pos;
- break;
- case ')':
- $args[] = substr($value, $from, $pos - $from);
- $pos++;
- $flength = $pos;
- break;
- default:
- $pos++;
- }
- }
- for ($pos=0; $pos<count($args); $pos++)
- {
- $args[$pos] = trim($args[$pos]);
- $args[$pos] = $this->Calc($args[$pos]);
- }
- $value = $this->cb_func($fname, $args);
- return (is_float($value) || is_int($value) ? $value : "'$value'");
- }
- /**
- * Выражение в скобках
- * @param string $value
- * @return string
- */
- private function subexp($value)
- {
- $value = substr($value, 1, -1);
- return $this->DoCalc($value);
- }
- /**
- * Члены тернарного выражения
- * @param string $value
- * @return string
- */
- private function ternval($value)
- {
- $value = substr($value, 1);
- $pos = 0;
- $length = strlen($value);
- while (true)
- {
- if ($pos >= $length) return "''";
- elseif ($value[$pos] === ' ') $pos++;
- else break;
- }
- $value = substr($value, $pos);
- return $this->DoCalc($value);
- }
- /**
- * тернарное выражение. Операция сравнения.
- * @param string $symbol
- * @param string $value1
- * @param string $value2
- * @return boolean
- */
- private function compare($symbol, $value1, $value2)
- {
- if (is_numeric($value1) && is_numeric($value2))
- {
- $value1 = floatval($value1);
- $value2 = floatval($value2);
- }
- switch ($symbol)
- {
- case '=':
- return ($value1 == $value2);
- case '!=':
- return ($value1 != $value2);
- case '<':
- return ($value1 < $value2);
- case '>':
- return ($value1 > $value2);
- default:
- return self::Error_Calc;
- }
- }
- private function pow($value1, $value2)
- {
- if (is_numeric($value1) && is_numeric($value2))
- return pow((float)$value1, (float)$value2);
- else
- return self::Error_Calc;
- }
- private function umn($value1, $value2)
- {
- if (is_numeric($value1) && is_numeric($value2))
- return floatval($value1) * floatval($value2);
- else
- return self::Error_Calc;
- }
- private function del($value1, $value2)
- {
- if (is_numeric($value1) && is_numeric($value2))
- return floatval($value1) / floatval($value2);
- else
- return self::Error_Calc;
- }
- private function plus($value1, $value2)
- {
- if (is_numeric($value1) && is_numeric($value2))
- return floatval($value1) + floatval($value2);
- else
- {
- $value1 = (is_numeric($value1) ? $value1 : substr($value1, 1, -1));
- $value2 = (is_numeric($value2) ? $value2 : substr($value2, 1, -1));
- return "'$value1$value2'";
- }
- }
- private function minus($value1, $value2)
- {
- if (is_numeric($value1) && is_numeric($value2))
- return floatval($value1) - floatval($value2);
- else
- return self::Error_Calc;
- }
- public function DoCalc($expression)
- {
- $pos = 0;
- $value = null;
- $result = '';
- while ($pos < strlen($expression))
- {
- if (($value = $this->parse($expression, $pos, $value)) === self::Error_Parse)
- break;
- $result = $value;
- }
- return $result;
- }
- public function Calc($expression)
- {
- $value = $this->DoCalc($expression);
- if (strlen($value) === 0) return '';
- elseif (is_numeric($value)) return $value;
- else return substr($value, 1, -1);
- }
- }
- //------------------- example -----------------------
- //class MyCalc extends Calculator
- //{
- // /**
- // * определяем пользовательские переменные
- // * @override
- // */
- // public function cb_var($value)
- // {
- // switch ($value)
- // {
- // case 'my_var':
- // return 'abc';
- // default:
- // return parent::cb_var($value);
- // }
- // }
- //
- // /**
- // * определяем пользовательские функции
- // * @override
- // */
- // public function cb_func($name, $args)
- // {
- // switch ($name)
- // {
- // case 'my_zzz':
- // return 'zzzzzzz';
- // case 'my_plus':
- // return (int)$args[0] + (int)$args[1];
- // default:
- // return parent::cb_func($name, $args);
- // }
- // }
- //}
- //
- ////если достаточно стандартного калькулятора
- ////$calc = new Calculator(null);
- //
- ////создаём свой калькулятор с расширенным набором функций
- //$calc = new MyCalc(null);
- //
- //echo $calc->Calc('(1 + 2) / 3 + 9 * 5');
- ////echo $calc->Calc('2+2(6^(1+1)*8-4)+7/4'); //степень
- ////echo $calc->Calc("(1 + 2)^3 * 5 + ' Текст'"); //вычисления + текст
- ////echo $calc->Calc("@replace('дороге', 'шоссе', 'Шла Саша по дороге')"); //замена текста
- ////echo $calc->Calc('(1+2)/3+9+@my_zzz()'); //вызов пользовательской функции
- ////echo $calc->Calc('(1+2)/3+9+@my_plus(100, 25)'); //вызов пользовательской функции с параметрами
- //
- ////вызов пользовательской переменной, а так же тернарное выражение
- ////echo $calc->Calc("'Какой-то текст ' + ([my_var]='abc' ? 'true' : 'false')"); //= (равно), != (не равно), < (меньше), > (больше)
Advertisement
Add Comment
Please, Sign In to add comment