Действительно полезным следующим шагом будет создание дерева разбора:
Вы бы сделали один из них, написав инфиксный парсер. Вы можете сделать это, написав простой парсер рекурсивного спуска или введя большие пушки и , используя генератор синтаксических анализаторов . В любом случае это помогает построить формальную грамматику:
expression: additive
additive: multiplicative ([+-] multiplicative)*
multiplicative: primary ('*' primary)*
primary: variable
| number
| '(' expression ')'
Обратите внимание, что эта грамматика не поддерживает синтаксис 2x
, но ее легко добавить.
Обратите внимание на умное использование рекурсии в правилах грамматики. primary
захватывает только переменные, числа и выражения в скобках и останавливается, когда он сталкивается с оператором. multiplicative
анализирует одно или несколько выражений primary
, ограниченных знаками *
, но останавливается, когда встречается знак +
или -
. additive
анализирует одно или несколько multiplicative
выражений, разделенных +
и -
, но останавливается, когда оно встречается в )
. Следовательно, схема рекурсии определяет приоритет оператора.
Не так уж сложно реализовать прогнозирующий синтаксический анализатор вручную, как я сделал ниже ( см. Полный пример на ideone.com ):
function parse()
{
global $tokens;
reset($tokens);
$ret = parseExpression();
if (current($tokens) !== FALSE)
die("Stray token at end of expression\n");
return $ret;
}
function popToken()
{
global $tokens;
$ret = current($tokens);
if ($ret !== FALSE)
next($tokens);
return $ret;
}
function parseExpression()
{
return parseAdditive();
}
function parseAdditive()
{
global $tokens;
$expr = parseMultiplicative();
for (;;) {
$next = current($tokens);
if ($next !== FALSE && $next->type == "operator" &&
($next->op == "+" || $next->op == "-"))
{
next($tokens);
$left = $expr;
$right = parseMultiplicative();
$expr = mkOperatorExpr($next->op, $left, $right);
} else {
return $expr;
}
}
}
function parseMultiplicative()
{
global $tokens;
$expr = parsePrimary();
for (;;) {
$next = current($tokens);
if ($next !== FALSE && $next->type == "operator" &&
$next->op == "*")
{
next($tokens);
$left = $expr;
$right = parsePrimary();
$expr = mkOperatorExpr($next->op, $left, $right);
} else {
return $expr;
}
}
}
function parsePrimary()
{
$tok = popToken();
if ($tok === FALSE)
die("Unexpected end of token list\n");
if ($tok->type == "variable")
return mkVariableExpr($tok->name);
if ($tok->type == "number")
return mkNumberExpr($tok->value);
if ($tok->type == "operator" && $tok->op == "(") {
$ret = parseExpression();
$tok = popToken();
if ($tok->type == "operator" && $tok->op == ")")
return $ret;
else
die("Missing end parenthesis\n");
}
die("Unexpected $tok->type token\n");
}
Хорошо, теперь у вас есть это прекрасное дерево разбора и даже симпатичная картинка, чтобы пойти с ним. Что теперь? Ваша цель (на данный момент) может состоять в том, чтобы просто объединить термины, чтобы получить результат в форме:
n1*a + n2*b + n3*c + n4*d + ...
Я оставлю эту часть вам. Наличие дерева разбора должно сделать вещи намного проще.