From a629e4c82f67fc914339f77897ffea65ba003ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 12 Jan 2013 09:13:42 +0100 Subject: [PATCH] Ajout de filter.py, qui contient un parseur de filtres. --- filter.py | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 filter.py diff --git a/filter.py b/filter.py new file mode 100644 index 0000000..8b0171f --- /dev/null +++ b/filter.py @@ -0,0 +1,244 @@ +#/usr/bin/env python +# -*- coding: utf8 -*- + +def human_to_ldap(filtre): + """ + Transforme les filtres "human readables" en + filtres respectant la syntaxe LDAP. + """ + # Piles, quand on croise une parenthèse ouvrante + # on change de pile + stacks = {0:''} + + # Position dans la pile + pos = 0 + + # operateur \in {&, |, &|, } + operateur = "" + + # La pile externe, qu'on merge à la pile en cours quand c'est utile. + ext_stack = "" + + # Élement de la forme paiement=2012 ou retour de la pile + # supérieure (quand on croise une parenthèse fermante, la + # pile est dumpée dans argument) + # Est systématiquement dumpé dans ext_stack quand on croise + # un opérateur. + argument = "" + + # Y a-t-il un ! dans la salle ? + neg = False + + # Quand on quitte une pile parenthésée, on veut savoir quel était + # l'opérateur actif avant, pour que le comportement de la fonction + # soit défini. + anciens_operateurs = [] + + for char in filtre: + if char == "(": + + # Une nouvelle stack ne démarre que si le dernier argument a été dumpé + # dans l'ext stack (si ce n'est pas le cas quand on respecte la syntaxe + # des filtres, la fonction est mal codée, donc on plante). + if argument != "": + raise ValueError('Argument entammé non terminé !') + + # Un dumpe ext_stack dans la stack en cours. + stacks[pos] += "%s" % (ext_stack) + + # On augmente le conteur de la stack + pos = pos + 1 + + # On (ré)?initialise la stack + stacks[pos] = '' + + # On stocke l'opérateur en cours dans la stack inférieure dans + # une liste. + anciens_operateurs.append(operateur) + + # On flush operateur + operateur = "" + + # On flush ext_stack, l'environnement est enfin propre pour + # bosser dans la nouvelle pile. + ext_stack = "" + + elif char == ")": + # Cas classique + if operateur == "|": + ext_stack += "(%s)" % argument + + # Moins classique, &| est un opérateur servant à dire qu'on + # a déjà croisé un | dans la stack en cours, le cas échéant, + # celui-ci doit être pris en compte, pour respecter la + # priorité du &. Dans ce cas seulement, une parenthèse + # est ouverte et non fermée (cf elif char == '&' cas + # operateur == "|"), on la ferme ici. + elif operateur == "&|": + argument += ")" + ext_stack += "(%s)" % argument + + # Classique + elif operateur == "&": + ext_stack += "(%s)" % argument + + # Pas d'opérateur, pas de parenthèse superflue. + else: + ext_stack += "%s" % argument + + # On passe la stack en argument, les parenthèses + # l'encadrant seront placées d'un qu'un opérateur + # sera croisé. + argument = "%s%s" % (stacks[pos], ext_stack) + + # Ménage + stacks[pos] = "" + ext_stack = "" + pos = pos - 1 + + # Retour à l'opérateur de la stack précédente. + operateur = anciens_operateurs.pop() + + elif char == "|": + if not argument: + raise ValueError('Aucun argument') + + # Ce cas permet d'éviter une répétition de la forme : + # (|(a)(|(b)(c))), quand on est déjà dans un ou, on + # rajoute juste l'argument suivant sans remettre de + # symbole. + if operateur == "|": + # neg est True si on a croisé un ! dans la chaîne. + # À améliorer pour qu'il ne marche que pour != + if neg: + argument = "!(%s)" % argument + neg = False + + # Ajout à la stack externe de l'argument + ext_stack += "(%s)" % argument + argument = "" + + elif operateur == "&": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "(%s)" % argument + + # | prend le relais sur &, on dumpe ext_stack et on commence une nouvelle + # chaîne qui sera ajoutée à droite. Le ou va tout à gauche de la stack + # en cours, pour s'appliquer sur tout son contenu. + stacks[pos] = "%s(%s%s)" % (char, stacks[pos], ext_stack) + ext_stack = "" + argument = "" + operateur = "|" + + # C'est un & dans un |, donc on ferme juste la chaîne + # des &, d'où la parenthèse fermante en trop. + elif operateur == "&|": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "(%s)" % argument + argument = "" + stacks[pos] = "%s%s)" % (stacks[pos], ext_stack) + ext_stack = "" + operateur = "|" + + # Pas encore d'opérateur annoncé + elif operateur == "": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "%s" % argument + argument = "" + + # ouverture + stacks[pos] = "%s(%s%s)" % (char, stacks[pos], ext_stack) + ext_stack = "" + operateur = "|" + + else: + raise TypeError('Erreur d\'opérateur.') + + elif char == "&": + if not argument: + raise ValueError('Aucun argument') + + if operateur == "&": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "(%s)" % argument + argument = "" + + # Le cas spécial, on ouvre un & après un |, donc pour respecter + # la priorité de &, on démarre une nouvelle chaîne, mais dans + # l'ext_stack (contrairement à char == '|', operateur == "&") + elif operateur == "|": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "(%s(%s)" % (char, argument) + argument = "" + operateur = "&|" + + # On était déjà dans un &|... + elif operateur == "&|": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "(%s)" % argument + argument = "" + + # Comme ci-dessus + elif operateur == "": + if neg: + argument = "!(%s)" % argument + neg = False + + ext_stack += "%s" % argument + argument = "" + + stacks[pos] = "%s(%s%s)" % (char, stacks[pos], ext_stack) + ext_stack = "" + operateur = "&" + + else: + raise TypeError('Erreur d\'opérateur.') + + elif char == "!": + neg = True + + # Remplissage d'argument + else: + argument += char + + # Décommenter pour débug. + # En modifiant un peu, encore plus utile pour savoir ce qu'il + # fait à chaque étape ! + #print stacks + #print pos, argument, ext_stack, operateur + + if pos > 0: + raise Exception("Tu ne sais pas parenthéser, crétin.") + + else: + # Comme parenthèse fermante. + if neg: + argument = "!(%s)" % argument + if operateur == "&|": + argument += ')' + ext_stack += "(%s)" % argument + + argument = "" + stacks[pos] = "(%s%s)" % (stacks[pos], ext_stack) + ext_stack = "" + + # On retourne la pile de plus haut niveau + return stacks[0]