2012-04-16 9 views
8

Próbuję użyć pyparsing do analizowania wywołań funkcji w postaci:analizowaniem funkcja zagnieżdżona wywołuje używając pyparsing

f(x, y) 

To proste. Ale ponieważ jest to zejście rekurencyjne-parser, powinno być również łatwe do analizowania:

f(g(x), y) 

To, czego nie można dostać. Oto gotowano-dół przykład:

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)") 

A oto wyjście:

['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) 

Dlaczego mój parser zinterpretować funktora wewnętrznej ekspresji jako samodzielny identyfikatora?

Odpowiedz

4

Definicja arg powinny być ułożone z rzeczą, która rozpoczyna się kolejna po lewej stronie, więc to jest dopasowany preferencyjnie:

arg = expression | identifier | integer 
+0

Niezły chwyt, @Jason! – PaulMcG

11

Niezły chwyt na zastanawianie się, że identifier została maskowanie expression w swojej definicji arg . Oto kilka wskazówek na temat swojej parsera:

x + ZeroOrMore(',' + x) jest bardzo częstym wzór w parserami pyparsing, więc pyparsing zawiera metodę pomocnika delimitedList który pozwala na zastąpienie tego wyrażenia z delimitedList(x). W rzeczywistości, delimitedList robi jeszcze jedną rzecz - tłumi przecinki ograniczające (lub inny ogranicznik, jeśli jest podany przy użyciu opcjonalnego argumentu delim), opierając się na założeniu, że separatory są użyteczne w czasie analizowania, ale są po prostu żetonami przy próbie przesiewania następnie przeanalizować dane. Możesz więc przepisać argumenty jako args = delimitedList(arg), a otrzymasz tylko argumenty na liście, bez przecinków, które będą musiały "przekroczyć".

Możesz użyć klasy Group, aby utworzyć rzeczywistą strukturę w analizowanych tokenach. Pozwoli to budować hierarchię lęgowe dla ciebie, bez konieczności iść do tej listy szuka „(” i „)”, aby powiedzieć, kiedy odeszłaś dół poziom zagnieżdżenia w funkcji:

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

Od swoimi args są poddawane Group ed dla Ciebie, można dodatkowo tłumią parens, ponieważ jak ograniczających przecinkami, robią swoją pracę podczas analizowania, ale ze zgrupowania swoich żetonów, nie są już konieczne:

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

Zakładam ' h() 'jest prawidłowym wywołaniem funkcji, po prostu nie ma argumentów. Można zezwolić args być opcjonalnie za pomocą Optional:

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

Teraz można analizować "f (g (x), y, h())".

Witamy w pyparsing!

+3

Dzięki za wszystkie pomocne komentarze!Ten przykład został właściwie zaadaptowany z dokumentacji pyparskiej; Używam większości technik, które opisujesz w moim rzeczywistym parserze. (A implementacja języka jest teraz możliwa do wykorzystania w około 6 godzin pracy - prototypowanie w Pythonie z pyparstwem jest zadziwiająco szybkie.) – JasonFruit

+0

jaka jest różnica między 'Suppress (" (")' a 'Literal (" ("). Suppress () '? – dashesy

+1

Bez żadnej różnicy.' Expr.suppress() 'zwraca' Suppress (expr) ', a jeśli łańcuch zostanie przekazany jako inicjator dla Suppress, łańcuch staje się promowany do Literału. – PaulMcG

0

Post Paul bardzo pomógł. Tylko dla odniesienia do innych, tak samo może być wykorzystane do określenia for loops następująco (uproszczony pseudo-parser tutaj, aby pokazać strukturę):

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_