2009-09-04 9 views
2

J'essaye d'écrire quelque chose qui analysera du code. Je suis en mesure d'analyser avec succès foo(spam) et spam+eggs, mais foo(spam+eggs) (descente récursive? Ma terminologie des compilateurs est un peu rouillée) échoue.PyParsing expressions de langage simples

je le code suivant:

from pyparsing_py3 import * 

myVal = Word(alphas+nums+'_')  
myFunction = myVal + '(' + delimitedList(myVal) + ')' 

myExpr = Forward() 
mySubExpr = (\ 
    myVal \ 
    | (Suppress('(') + Group(myExpr) + Suppress(')')) \ 
    | myFunction \ 
    ) 
myExpr << Group(mySubExpr + ZeroOrMore(oneOf('+ -/* =') + mySubExpr)) 


# SHOULD return: [blah, [foo, +, bar]] 
# but actually returns: [blah] 
print(myExpr.parseString('blah(foo+bar)')) 

Répondre

4

Plusieurs questions: delimitedList recherche une liste délimitée par des virgules myVal, soit des identifiants, comme la seule forme acceptable de liste d'arguments, donc bien sûr, il peut » t correspond à 'foo + bar' (pas une liste délimitée par des virgules de myVal!); la fixation qui en révèle une autre - myVal et myFunction commencent de la même façon, donc leur commande dans mySubExpr importe; fixation qui révèle encore un autre - DEUX niveaux de nidification au lieu d'un. Cette version semble ok ...:

myVal = Word(alphas+nums+'_')  

myExpr = Forward() 
mySubExpr = (
    (Suppress('(') + Group(myExpr) + Suppress(')')) 
    | myVal + Suppress('(') + Group(delimitedList(myExpr)) + Suppress(')') 
    | myVal 
    ) 
myExpr << mySubExpr + ZeroOrMore(oneOf('+ -/* =') + mySubExpr) 

print(myExpr.parseString('blah(foo+bar)')) 

émet ['blah', ['foo', '+', 'bar']] comme on le souhaite. J'ai également supprimé les antislashs redondants, puisque la continuation de ligne logique se produit quand même entre parenthèses; ils étaient inoffensifs mais gênaient la lisibilité.

4

J'ai trouvé qu'une bonne habitude à prendre quand on utilise l'opérateur '< <' avec Forwards est de toujours placer le RHS entre parenthèses. C'est:

myExpr << mySubExpr + ZeroOrMore(oneOf('+ -/* =') + mySubExpr) 

est mieux que:

myExpr << (mySubExpr + ZeroOrMore(oneOf('+ -/* =') + mySubExpr)) 

Ceci est le résultat de mon choix malheureux de « < < » comme l'opérateur « d'insertion » pour insérer l'expression dans un avant. Les parenthèses ne sont pas nécessaires dans ce cas particulier, mais dans celui-ci:

integer = Word(nums) 
myExpr << mySubExpr + ZeroOrMore(oneOf('+ -/* =') + mySubExpr) | integer 

nous voyons pourquoi je dis « malheureux ». Si je simplifie cela à "A < < B | C", nous voyons facilement que la préséance des opérations provoque l'évaluation comme "(A < < B) | C", puisque '< <' a une priorité supérieure à '| '. Le résultat est que le Forward A obtient seulement l'expression B insérée dedans. La partie "| C" est exécutée, mais ce qui se passe est que vous obtenez "A | C" qui crée un objet MatchFirst, qui est immédiatement supprimé car il n'est affecté à aucun nom de variable. La solution consisterait à regrouper l'instruction entre parenthèses sous la forme "A < < (B | C)". Dans les expressions composées uniquement à l'aide des opérations '+', les parenthèses ne sont pas réellement nécessaires car '+' a une priorité supérieure à '< <'. Mais ce n'est que du codage chanceux, et cause des problèmes quand quelqu'un ajoute plus tard une expression alternative en utilisant '|' et ne réalise pas les implications de préséance. Donc, je suggère simplement d'adopter le style "A < < (expression)" pour aider à éviter cette confusion.

(Un jour, je vais écrire pyparsing 2.0 - ce qui me permettra de rompre compatibilty avec le code existant - et changer cette option pour utiliser l'opérateur « < < = », qui fixe toutes ces questions de priorité, étant donné que « < < = » a une priorité inférieure à celle des autres opérateurs utilisés par pyparsing.)

+1

Paul, C'est une information très utile sur la priorité d'opérateur que je n'avais pas considérée. Merci pour cela, et merci aussi pour votre formidable contribution à la communauté Python! J'ai aimé 'Getting Started with Pyparsing', mais j'ai apparemment besoin d'un peu plus d'expérience. –