2012-04-16 10 views
8

Estoy intentando utilizar pyparsing para analizar las llamadas a funciones en la forma:de análisis de la función anidada llama usando pyparsing

f(x, y) 

eso es fácil. Pero ya que es un analizador sintáctico descendente recursivo, que también debe ser fácil de analizar:

f(g(x), y) 

Eso es lo que no se pueda conseguir. Aquí está un ejemplo hervida abajo:

from pyparsing import Forward, Word, alphas, alphanums, nums, ZeroOrMore, Literal 

lparen = Literal("(") 
rparen = Literal(")") 

identifier = Word(alphas, alphanums + "_") 
integer = Word(nums) 

functor = identifier 

# allow expression to be used recursively 
expression = Forward() 

arg = identifier | integer | expression 
args = arg + ZeroOrMore("," + arg) 

expression << functor + lparen + args + rparen 

print expression.parseString("f(x, y)") 
print expression.parseString("f(g(x), y)") 

Y aquí está la salida:

['f', '(', 'x', ',', 'y', ')'] 
Traceback (most recent call last): 
    File "tmp.py", line 14, in <module> 
    print expression.parseString("f(g(x), y)") 
    File "/usr/local/lib/python2.6/dist-packages/pyparsing-1.5.6-py2.6.egg/pyparsing.py", line 1032, in parseString 
    raise exc 
pyparsing.ParseException: Expected ")" (at char 3), (line:1, col:4) 

¿Por qué mi analizador interpretar el functor de la expresión interna como un identificador independiente?

Respuesta

4

La definición de arg debe ser arreglado con el elemento que se inicia con otra a la izquierda, por lo que se corresponde preferentemente:

arg = expression | identifier | integer 
+0

Buena atrapada, @ Jason! – PaulMcG

11

Buena atrapada en averiguar que identifier enmascaraba expression en su definición de arg . Aquí hay algunos consejos sobre su programa de análisis:

x + ZeroOrMore(',' + x) es un patrón muy común en los programas de análisis pyparsing, por lo pyparsing incluye un método de ayuda delimitedList que le permite sustituir esa expresión con delimitedList(x). En realidad, delimitedList hace otra cosa: suprime las comillas de delimitación (u otro delimitador si se usa con el argumento opcional delim), basándose en la noción de que los delimitadores son útiles en el tiempo de análisis, pero son solo tokens de desorden cuando se intenta examinar el datos analizados luego. Así que puedes reescribir args como args = delimitedList(arg), y obtendrás los args en una lista, sin comas que tener que "pisar".

Puede usar la clase Group para crear una estructura real en los tokens analizados. Esto va a construir su jerarquía de anidamiento para usted, sin tener que caminar esta lista en busca de '(' y ')' que le diga cuando has ido a un nivel inferior en la función de anidación:

arg = Group(expression) | identifier | integer 
expression << functor + Group(lparen + args + rparen) 

Desde sus argumentos están siendo Group ed para usted, usted puede suprimir aún más las parens, ya que como las comas que delimitan, que hacen su trabajo durante el análisis, pero con la agrupación de sus fichas, que ya no son necesarios:

lparen = Literal("(").suppress() 
rparen = Literal(")").suppress() 

asumo ' h() 'es una llamada de función válida, simplemente no args. Puede permitir que los argumentos sean opcional usando Optional:

expression << functor + Group(lparen + Optional(args) + rparen) 

Ahora se puede analizar "f (g (x), y, h())".

Bienvenido a pyparsing!

+3

¡Gracias por todos los útiles comentarios!Este ejemplo fue realmente adaptado de la documentación de pyparsing; Uso la mayoría de las técnicas que describes en mi analizador real. (Y la implementación del lenguaje ahora se puede utilizar en aproximadamente 6 horas de trabajo --- el prototipado en Python con pyparsing es increíblemente rápido.) – JasonFruit

+0

¿cuál es la diferencia entre 'Suppress (" (")' y 'Literal (" ("). Suprimir () '? – dashesy

+1

Sin diferencia alguna.' Expr.suppress() 'devuelve' Suppress (expr) ', y si se pasa una cadena como el inicializador para Suppress, la cadena se promueve a Literal. – PaulMcG

0

La publicación de Paul ayudó mucho. Sólo por la referencia de los demás, lo mismo puede usarse para definir for loops de la siguiente manera (pseudo-analizador simplificado aquí, para mostrar la estructura):

sep = Literal(';') 
if_ = Keyword('if') 
then_ = Keyword('then') 
elif_ = Keyword('elif') 
end_ = Keyword('end') 

if_block = Forward() 
do_block = Forward() 

stmt = other | if_block 
stmts = OneOrMore(stmt +sep) 

case = Group(guard +then_ +stmts) 
cases = case +OneOrMore(elif_ +case) 

if_block << if_ +cases +end_