Ensuite les expressions régulières.
Afin de faciliter le test de votre programme, je vous demande de vous
conformer à la syntaxe suivante :
Les expression régulières p sont engendrées par la grammaire suivante :
p = | c | . | p* | p? | pp | p|p | (p)
Où «» (oui rien) est le motif vide,
c est un caractère quelconque (éventuellement cité),
. est le caractère attrape tout,
p* est la répétition,
p? est l'option,
pp est la concaténation de deux motifs,
p|p est l'alternative de deux motifs et
(p) est un simple parenthésage.
Comme cette grammaire est ambigüe, on convient de donner des priorités
aux divers opérateurs, dans l'ordre décroissant, d'abord
repétition et option (* et ? postfixes), puis
concaténation (juxtaposition) et enfin alternative
(| infixe).
C'est à dire par exemple que
ab*|cd se comprend comme ((a)(b)*)|(cd).
Il faudrait pour faire bonne mesure définir également l'associativité
des opérations infixes, mais cela n'a aucune importance, car comme on
va le voir (a|b)|c et a|(b|c) on la même
signification,
on peut donc interpréter le motif non parenthésé a|b|c comme
l'un où l'autre des motifs parenthésés précédents.
En effet, donnons nous un ensemble de caractères C soit alors M
l'ensemble des mots que l'on peut former avec les caractères de C
(suites finies de caractères de C, qui comprend la suite (le mot) vide notée є).
On peut associer à chaque motif p l'ensemble F(p) des mots qu'il filtre :
-
Le motif vide filtre le mot vide.
- F(c) = {c}.
- F(.) = C.
- F(p*) = {m1m2… mk ∣ k ≥ 0, mi ∈ F(p) }
(ensemble des concaténations des mots de F(p)).
- F(p?) = {є} ∪ F(p)
- F(p1p2) = {m1m2 ∣ m1 ∈ F(p1) et m2 ∈
F(p2)}
- F(p1|p2) = F(p1) ∪ F(p2).
On notera que le # du niveau précédent est équivalent à
.*.