M>Опиши, какие операторы будешь использовать, и их приоритет. Напиши, что хочешь использовать группирующие скобки. Скорми это дипсику, и попроси его написать парсер рекурсивного спуска. Он справится. Я так делал. Единственно, у меня уже был лексер, и на выходе лексера (на входе парсера) уже были токены — оператор/литерал/идентификатор/скобка. Он мне на питоне написал, но я без проблем переделал под себя на плюсиках. Возможно, на PHP он сразу сможет написать. Ну, и пусть тебе лексер напишет, или совместит его с парсером.
M>Да, не забудь указать, что +/- могут быть унарными.
M>Скажи ему, что значения хочешь складывать на стек, вызывать операторную функцию, и помещать результат обратно на стек.
я попробую конечно чисто заради любопытства посмотреть, на что он способен этот ваш гигачат, но мне хотелось бы что-то совсем коротко чтобы.
Может какую-то библиотечку подключить, где есть нужная мне функция.
А то все эти грамматики-парсеры-лексеры я уже давно забыл, если и знал когда-то.
На худой конец оставлю eval, только предварительно проверку сделаю, чтобы там в качестве параметра ничего кроме 0..9,+,-,*,/,(,) не могло пролезть
ЗЫ. Ниже результат работы gigachat
// Функция для обработки арифметического выражения
function calculateExpression($expression)
{
// Очищаем пробелы
$expression = str_replace(' ', '', $expression);
try {
// Используем встроенную функцию eval() осторожно
return eval("return ($expression);");
} catch (\\ParseError | \\DivisionByZeroError $e) {
throw new Exception("Ошибка вычисления выражения: {$e->getMessage()}");
}
}
или без использования eval()
class Calculator {
private function isOperator($char) {
return in_array($char, ['+', '-', '*', '/']);
}
private function precedence($op) {
if ($op === '+' || $op === '-') return 1;
if ($op === '*' || $op === '/') return 2;
return 0;
}
private function applyOperation(&$stackValues, &$stackOperators) {
$right = array_pop($stackValues);
$left = array_pop($stackValues);
$operator = array_pop($stackOperators);
switch ($operator) {
case '+':
array_push($stackValues, $left + $right);
break;
case '-':
array_push($stackValues, $left - $right);
break;
case '*':
array_push($stackValues, $left * $right);
break;
case '/':
if ($right != 0) {
array_push($stackValues, $left / $right);
} else {
throw new Exception("Деление на ноль!");
}
break;
}
}
public function evaluate($expression) {
$tokens = preg_split('/(\\d+(?:\\.\\d+)?|\\(|\\)|[+\\-*\\/])/', trim($expression), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$stackValues = [];
$stackOperators = [];
foreach ($tokens as $token) {
if (is_numeric($token)) { // Число
array_push($stackValues, floatval($token));
} elseif ($this->isOperator($token)) { // Операция
while (!empty($stackOperators) && $this->precedence(end($stackOperators)) >= $this->precedence($token)) {
$this->applyOperation($stackValues, $stackOperators);
}
array_push($stackOperators, $token);
} elseif ($token === '(') { // Открывающая скобка
array_push($stackOperators, $token);
} elseif ($token === ')') { // Закрывающая скобка
while (end($stackOperators) !== '(') {
$this->applyOperation($stackValues, $stackOperators);
}
array_pop($stackOperators); // Убираем открывающую скобку
}
}
while (!empty($stackOperators)) {
$this->applyOperation($stackValues, $stackOperators);
}
return end($stackValues);
}
}