Una muy útil siguiente paso sería construir un árbol de análisis:
Se podría hacer una de estas escribiendo un analizador infija. Usted puede hacer esto escribiendo un analizador sintáctico de descenso recursivo simple, o trayendo las armas grandes y using a parser generator. En cualquiera de los casos, ayuda a construir una gramática formal:
expression: additive
additive: multiplicative ([+-] multiplicative)*
multiplicative: primary ('*' primary)*
primary: variable
| number
| '(' expression ')'
Tenga en cuenta que esta gramática no maneja la sintaxis 2x
, pero debe ser fácil de añadir.
Observe el uso inteligente de recursividad en las reglas de la gramática. primary
solo captura variables, números y expresiones entre paréntesis, y se detiene cuando se ejecuta en un operador. multiplicative
análisis sintáctico de una o más expresiones delimitadas por primary
*
señales, pero se detiene cuando se encuentra con un signo +
o -
. additive
análisis sintáctico de una o más multiplicative
expresiones delimitadas por +
y -
, pero se detiene cuando se ejecuta en un )
. Por lo tanto, el esquema de recursión determina la precedencia del operador.
No es tan terriblemente difícil implementar un predictive parser a mano, como lo he hecho a continuación (see full example at 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");
}
Bien, ahora usted tiene este precioso árbol de análisis, e incluso una muy imagen para ir con eso. ¿Ahora que?Su objetivo (por ahora) podría ser simplemente combinar términos de obtener un resultado de la forma:
n1*a + n2*b + n3*c + n4*d + ...
voy a dejar esa parte de ti. Tener un árbol de análisis debería hacer las cosas mucho más sencillas.
El token VAR tiene una cadena asociada. ¿Por qué su token NUMBER no tiene un número asociado? –
Dependiendo de sus limitaciones, ¿por qué no intenta construir un [OOP Interpreter] (http://sourcemaking.com/design_patterns/interpreter)? Debería ser más fácil que tratar con tokens, y el árbol debería representarse a sí mismo. También debería ser bastante fácil trabajar con él. – ircmaxell
@David Heffernan: Una de las ventajas de PHP al tratar con expresiones y lenguajes de programación son las variables sigil. Puede nombrar sus variables '$ operator' y' $ var' sin tener que preocuparse por el conflicto con las palabras clave en el lenguaje de programación. –