3 votes

Analyse des expressions arithmétiques avec des appels de fonction

Je travaille avec pyparsing et je l'ai trouvé excellent pour développer un DSL simple qui me permet d'extraire des champs de données de MongoDB et de faire des opérations arithmétiques simples sur eux. J'essaie maintenant d'étendre mes outils de manière à pouvoir appliquer des fonctions de la forme Rank[Person:Height] aux champs et potentiellement inclure des expressions simples en tant qu'arguments aux appels de fonction. J'ai beaucoup de mal à faire fonctionner la syntaxe d'analyse. Voici ce que j'ai jusqu'à présent :

# Define parser
expr = Forward()
integer = Word(nums).setParseAction(EvalConstant)
real = Combine(Word(nums) + "." + Word(nums)).setParseAction(EvalConstant)

# Handle database field references that are coming out of Mongo, 
# accounting for the fact that some fields contain whitespace
dbRef = Combine(Word(alphas) + ":" + Word(printables) + \
    Optional(" " + Word(alphas) + " " + Word(alphas)))
dbRef.setParseAction(EvalDBref)

# Handle function calls
functionCall = (Keyword("Rank") | Keyword("ZS") | Keyword("Ntile")) + "[" + expr + "]"
functionCall.setParseAction(EvalFunction)
operand =  functionCall | dbRef | (real | integer) 

signop = oneOf('+ -')
multop = oneOf('* /')
plusop = oneOf('+ -')

# Use parse actions to attach Eval constructors to sub-expressions
expr << operatorPrecedence(operand,
    [
     (signop, 1, opAssoc.RIGHT, EvalSignOp),
     (multop, 2, opAssoc.LEFT, EvalMultOp),
     (plusop, 2, opAssoc.LEFT, EvalAddOp),
    ])

Mon problème est que lorsque je teste une expression simple comme Rank[Person:Height], j'obtiens une exception d'analyse :

ParseException: Expected "]" (at char 19), (line:1, col:20)

Si j'utilise un flottant ou une expression arithmétique comme argument, comme Rank[3 + 1.1], l'analyse fonctionne correctement, et si je simplifie la grammaire de dbRef pour qu'il s'agisse simplement de Word(alphas), cela fonctionne également. Je n'arrive pas à comprendre ce qui ne va pas avec ma grammaire complète. J'ai essayé de réarranger l'ordre des opérandes ainsi que de simplifier la grammaire functionCall, sans succès. Quelqu'un peut-il me dire ce qui ne va pas ?

Une fois que cela fonctionnera, je voudrais faire un dernier pas et introduire le support pour l'assignation de variables dans les expressions .

EDIT : Après d'autres tests, si je supprime les imprimables de la grammaire de dbRef, les choses fonctionnent correctement :

 dbRef = Combine(Word(alphas) + OneOrMore(":") + Word(alphanums) + \
      Optional("_" + Word(alphas)))

CEPENDANT, si j'ajoute le caractère "-" à dbRef (dont j'ai besoin pour les champs de la base de données comme "Class:S-N"), l'analyseur échoue à nouveau. Je pense que le "-" est consommé par le signop dans mon operatorPrecedence ?

2voto

mjv Points 38081

Ce qui semble se passer, c'est que le ] à la fin de votre chaîne de test ( Rank[Person:Height] ) est consommé dans le cadre de la dbRef car la partie de ce jeton qui dépasse le jeton initial : est déclaré comme étant composé de Word(printables) (et ce jeu de caractères inclut malheureusement les crochets)

Ensuite, l'analyseur syntaxique tente de produire un functionCall mais il manque la clôture ] d'où le message d'erreur.

Une solution provisoire consiste à utiliser un jeu de caractères qui n'inclut pas les crochets, peut-être quelque chose de plus explicite comme :

dbRef = Combine(Word(alphas) + ":" + Word(alphas, alphas+"-_./") + \
    Optional(" " + Word(alphas) + " " + Word(alphas)))

Editer :
En y regardant de plus près, ce qui précède est à peu près correct, mais la hiérarchie des jetons est erronée (par exemple, l'analyseur syntaxique tente de produire un functionCall comme l'un des opérandes d'un an expr etc.)
En outre, la solution que j'ai proposée ne fonctionnera pas en raison de l'ambiguïté de l'élément - qui doit être considéré comme un caractère ordinaire lorsqu'il se trouve à l'intérieur d'un dbRef et en tant que plusOp lorsqu'il se trouve à l'intérieur d'un expr . Ce type de problème est courant avec les analyseurs syntaxiques et il existe des moyens d'y remédier, bien que je ne sois pas sûr de savoir exactement comment procéder avec pyparsing.

0voto

Roger Sanchez Points 311

J'ai trouvé la solution - le problème était que ma grammaire pour dbRef consommait certains des caractères qui faisaient partie de la spécification de la fonction. Nouvelle grammaire qui fonctionne correctement :

dbRef = Combine(Word(alphas) + OneOrMore(":") + Word(alphanums) + \
    Optional(oneOf("_ -") + Word(alphas)))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X