2012-04-16 12 views
8

Sto cercando di utilizzare pyparsing per analizzare le chiamate di funzione nella forma:di analisi funzione nidificata chiama usando pyparsing

f(x, y) 

che è facile. Ma dal momento che si tratta di un parser ricorsivo-discesa, dovrebbe anche essere facile da analizzare:

f(g(x), y) 

Questo è quello che non riesco a ottenere. Ecco un esempio bollito-down:

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

Ed ecco l'output:

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

Perché il mio parser interpreta il funtore dell'espressione interiore come un identificatore standalone?

risposta

4

La definizione di arg dovrebbe essere organizzato con la voce che inizia con un altro a sinistra, per cui è abbinato preferenzialmente:

arg = expression | identifier | integer 
+0

Bella presa, @ Jason! – PaulMcG

11

Bella presa sul capire che identifier stava mascherando expression nella tua definizione di arg . Ecco alcuni altri suggerimenti sul parser:

x + ZeroOrMore(',' + x) è un modello molto comune nel parser pyparsing, così pyparsing include un metodo di supporto delimitedList che permette di sostituire questa espressione con delimitedList(x). In realtà, delimitedList fa un'altra cosa: sopprime le virgole delimitanti (o altro delimitatore se viene dato usando l'argomento opzionale delim), basato sulla nozione che i delimitatori sono utili al tempo di analisi, ma sono solo token di clutter quando si cerca di setacciare il dati analizzati in seguito. Quindi puoi riscrivere args come args = delimitedList(arg), e otterrai solo gli argomenti in una lista, senza virgole da "scavalcare".

È possibile utilizzare la classe Group per creare la struttura effettiva nei token analizzati. Questo sarà costruire la vostra gerarchia di annidamento per voi, senza dover camminare questa lista alla ricerca di '(' e ')' di dirvi quando sei andato giù un livello nella funzione di nidificazione:

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

Dal momento che i args vengono Group ed per voi, si può ulteriormente sopprimere la parentesi, dal momento che, come le virgole che delimitano, fanno il loro lavoro durante l'analisi, ma con il raggruppamento dei tuoi gettoni, non sono più necessari:

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

presumo ' h() 'è una chiamata di funzione valida, solo no args. È possibile consentire args per essere opzionale utilizzando Optional:

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

Ora è possibile analizzare "f (g (x), y, h())".

Benvenuto in pyparsing!

+3

Grazie per tutti i commenti utili!Questo esempio è stato adattato dalla documentazione di pyparsing; Uso la maggior parte delle tecniche che descrivi nel mio parser effettivo. (E l'implementazione del linguaggio è ora utilizzabile in circa 6 ore di lavoro --- la prototipazione in Python con pyparsing è incredibilmente veloce.) – JasonFruit

+0

qual è la differenza tra 'Suppress (" (")' e 'Literal (" ("). Suppress ('' – dashesy

+1

Nessuna differenza. 'Expr.suppress()' restituisce 'Suppress (expr)', e se una stringa viene passata come inizializzatore per Suppress, la stringa viene promossa a Literal. – PaulMcG

0

Il post di Paul ha aiutato molto. Proprio per il riferimento degli altri, lo stesso può essere usato per definire for loops come segue (semplificato pseudo-parser qui, per mostrare la struttura):

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_