Ajout de filter.py, qui contient un parseur de filtres.

This commit is contained in:
Pierre-Elliott Bécue 2013-01-12 09:13:42 +01:00
parent 12b8166e82
commit a629e4c82f

244
filter.py Normal file
View file

@ -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]