El camino difícil
Quieres un analizador de descenso recursivo .
Para obtener precedencia, debe pensar de forma recursiva, por ejemplo, utilizando su cadena de muestra,
1+11*5
para hacer esto manualmente, tendría que leer el 1
, luego ver el más y comenzar una nueva "sesión" de análisis recursivo comenzando con 11
... y asegurarse de analizar el 11 * 5
en su propio factor, produciendo un árbol de análisis con 1 + (11 * 5)
.
Todo esto se siente tan doloroso incluso para intentar explicarlo, especialmente con la impotencia adicional de C. Ver, después de analizar el 11, si el * fuera realmente un + en su lugar, tendría que abandonar el intento de hacer un término y en su lugar analizar el 11
sí mismo como un factor. Mi cabeza ya esta explotando. Es posible con la estrategia recursiva decente, pero hay una mejor manera ...
La forma fácil (correcta)
Si usa una herramienta GPL como Bison, probablemente no necesite preocuparse por problemas de licencia, ya que el código C generado por bison no está cubierto por la GPL (IANAL, pero estoy bastante seguro de que las herramientas GPL no fuerzan la GPL en código generado / binarios; por ejemplo, Apple compila código como, por ejemplo, Aperture con GCC y lo venden sin tener que GPL dicho código).
Descarga Bison (o algo equivalente, ANTLR, etc.).
Por lo general, hay un código de muestra en el que puede ejecutar bison y obtener el código C deseado que demuestra esta calculadora de cuatro funciones:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Mire el código generado y vea que esto no es tan fácil como parece. Además, las ventajas de usar una herramienta como Bison son: 1) aprendes algo (especialmente si lees el libro Dragon y aprendes sobre las gramáticas), 2) evitas que los NIH intenten reinventar la rueda. Con una herramienta generadora de analizadores sintácticos real, en realidad tiene la esperanza de escalar más adelante, mostrando a otras personas que sabe que los analizadores son el dominio de las herramientas de análisis sintáctico.
Actualizar:
La gente aquí ha ofrecido muchos buenos consejos. Mi única advertencia contra saltarse las herramientas de análisis o simplemente usar el algoritmo Shunting Yard o un analizador decente recursivo enrollado a mano es que los pequeños lenguajes de juguete 1 pueden convertirse algún día en grandes lenguajes reales con funciones (sin, cos, log) y variables, condiciones y para bucles.
Flex / Bison puede ser excesivo para un intérprete pequeño y simple, pero un analizador + evaluador único puede causar problemas en el futuro cuando es necesario realizar cambios o agregar funciones. Su situación variará y deberá utilizar su criterio; simplemente no castigue a otras personas por sus pecados [2] y construya una herramienta menos que adecuada.
Mi herramienta favorita para analizar
La mejor herramienta del mundo para el trabajo es la biblioteca Parsec (para analizadores decentes recursivos) que viene con el lenguaje de programación Haskell. Se parece mucho a BNF , oa alguna herramienta especializada o lenguaje específico de dominio para analizar (código de muestra [3]), pero de hecho es solo una biblioteca normal en Haskell, lo que significa que compila en el mismo paso de compilación que el resto. de su código Haskell, y puede escribir código Haskell arbitrario y llamarlo dentro de su analizador, y puede mezclar y combinar otras bibliotecas en el mismo código . (Por cierto, incrustar un lenguaje de análisis como este en un lenguaje que no sea Haskell da como resultado un montón de cruft sintáctico. Hice esto en C # y funciona bastante bien, pero no es tan bonito y sucinto).
Notas:
1 Richard Stallman dice, en Por qué no debería usar Tcl
La principal lección de Emacs es que un lenguaje para extensiones no debe ser un mero "lenguaje de extensión". Debe ser un lenguaje de programación real, diseñado para escribir y mantener programas importantes. ¡Porque la gente querrá hacer eso!
[2] Sí, siempre estoy asustado por usar ese "lenguaje".
También tenga en cuenta que cuando presenté esta entrada, la vista previa era correcto, pero SO de menos de analizador adecuada comió mi estrecha etiqueta de anclaje en el primer párrafo , lo que demuestra que los analizadores no son algo que se podía jugar porque si utiliza expresiones regulares y uno de los cortes que probablemente obtendrá algo sutil y pequeño mal .
[3] Fragmento de un analizador de Haskell que usa Parsec: una calculadora de cuatro funciones extendida con exponentes, paréntesis, espacios en blanco para multiplicar y constantes (como pi y e).
aexpr = expr `chainl1` toOp
expr = optChainl1 term addop (toScalar 0)
term = factor `chainl1` mulop
factor = sexpr `chainr1` powop
sexpr = parens aexpr
<|> scalar
<|> ident
powop = sym "^" >>= return . (B Pow)
<|> sym "^-" >>= return . (\x y -> B Pow x (B Sub (toScalar 0) y))
toOp = sym "->" >>= return . (B To)
mulop = sym "*" >>= return . (B Mul)
<|> sym "/" >>= return . (B Div)
<|> sym "%" >>= return . (B Mod)
<|> return . (B Mul)
addop = sym "+" >>= return . (B Add)
<|> sym "-" >>= return . (B Sub)
scalar = number >>= return . toScalar
ident = literal >>= return . Lit
parens p = do
lparen
result <- p
rparen
return result