\section{Les expressions régulières}

Dans cette section, nous parlerons d'expressions régulières (\textit{ER}). Nous
allons nous concentrer sur un type bien particulier d'expressions régulières
qui ne seront pas les expressions régulières que nous pouvons voir plus
quotidiennement dans le domaine de l'informatique, les expressions régulières
\textit{UNIX}. Mais plut\^{o}t une version plus simple de celles-ci.

\subsection{Définition}

Nous allons noter une expression régulière \(E \in Exp(\Sigma)\), c'est-à-dire
une expression régulière où les symboles sont inclus dans l'ensemble \(\Sigma\)
et où \(Exp(\Sigma)\) représente l'ensemble des expressions sur \(\Sigma\).
Cette expression reconnait un langage qu'on pourra appeler \(L(E)\). Nous
pouvons définir une expression régulière récursivement de cette manière~:

\begin{align}
    E & = \varepsilon                 \\
    E & = a                           \\
    E & = F + G                       \\
    E & = F \cdot G                   \\
    E & = F^*                         \\
    E & = (F)\label{align:parenthese} \\
    \makebox[0pt][c]{
        \phantom{E } \text{
            avec \(E\), \(F\) et \(G\) des expressions régulières sur \(\Sigma\) et \(a\)
            un symbole de \(\Sigma\)
        }
    } \notag
\end{align}

On notera que \(*\) est prioritaire sur \(\cdot\) qui est lui-même prioritaire
sur \(+\) et qu'ils sont tous deux associatifs à gauche. On comprend donc
pourquoi l'équation~(\ref{align:parenthese}) existe, elle est là pour des
raisons de priorité. Il est alors évident de calculer les diverses fonctions
sur celle-ci, c'est pour cela qu'on ne précisera pas son
calcul\label{subsec:parenthese}. On peut définir chaque équation comme ceci~:

\vphantom{}

\begin{itemize}
    \item[\textbullet] \textbf{\(E = \varepsilon\)~:}
        Représente le mot vide, de ce fait un mot de longueur zéro. Il peut être
        parfois représenté par \og{}\$\fg{}.

        \vphantom{}

    \item[\textbullet] \textbf{\(E = a\)~:} Représente un symbole
        présent dans l'ensemble \(\Sigma\)

        \vphantom{}

    \item[\textbullet] \textbf{\(E = F + G\)~:} Représente l'union
        des deux expressions régulières \(F\) et \(G\). Par abus de langage, on
        peut aussi dire \(F\) \og{}ou\fg{} \(G\) pour représenter cette union.

        \vphantom{}

    \item[\textbullet] \textbf{\(E = F \cdot G\)~:}
        Représente la concaténation des deux expressions régulières \(F\) et
        \(G\).

        \vphantom{}

    \item[\textbullet] \textbf{\(E = F^* \)~:} Représente l'union infinie de
        copie de \(F\), cette répétition incluant la puissance zéro et donc le
        mot vide.
\end{itemize}

\vphantom{}

Pour calculer le langage que dénote l'expression régulière, on peut le calculer
récursivement de cette manière~:

\begin{align*}
    L(\varepsilon) & = \{\varepsilon\} \\
    L(a) & = \{a\}           \\
    L(F + G) & = L(F) \cup L(G)  \\
    L(F \cdot G) & = L(F) \cdot L(G) \\
    L(F^*) & = (L(F))^*        \\
    \makebox[0pt][c]{
        \phantom{E } \text{
            avec \(F\) et \(G\) des expressions régulières sur \(\Sigma\) et \(a\)
            un symbole de \(\Sigma\)
        }
    } \notag
\end{align*}

\begin{example}
    On comprendra ainsi que l'expression \(E = a+c \cdot d\) avec \(E \in
    Exp(\Sigma)\) et \(\Sigma = \{a, b, c, d\}\), dénote le langage \(L(E) =
    \{a, cd\}\). Car on peut représenter \(E\) comme ceci~:

    \begin{figure}[H]
        \centering
        \captionsetup{type=figure,justification=centering}
        \begin{tikzpicture}[
                mycircle/.style={
                        draw,
                        circle,
                        minimum height=.75cm,
                        minimum width=.75cm
                    },
                mysquare/.style={
                        draw,
                        rectangle,
                        minimum height=.5cm,
                        minimum width=.5cm,
                    }
            ]
            \node[mysquare] (plus) {+}
            child {node[mycircle] (a) {a}}
            child {
                    node[mysquare] (point) {.}
                    child {node[mycircle] (c) {c}}
                    child {node[mycircle] (d) {d}}
                };

            \node[right=3mm of d] {\(\{d\}\)};
            \node[left=3mm of c] {\(\{c\}\)};
            \node[right=3mm of point] {\(\{cd\}\)};
            \node[left=3mm of a] {\(\{a\}\)};
            \node[above=3mm of plus] {\(\{a, cd\}\)};
        \end{tikzpicture}
        \caption{
            Représentation de l'expression régulière à l'aide d'un arbre syntaxique
        }\label{fig:arbre_syn}
    \end{figure}

    Comme on peut voir sur la Figure~\ref{fig:arbre_syn}, grâce à cette
    représentation, on peut calculer simplement le langage reconnu par
    l'expression régulière (ici représenté par les ensembles à c\^{o}té de
    chaque arbre).
\end{example}

% \vphantom{}

% \begin{example}

%     On comprendra aussi que l'expression \(E' = \varepsilon + b^* \cdot a\), dénote
%     le langage \(L(E') = \{\varepsilon, b^* \cdot a\}\).

%     \begin{figure}[H]
%         \centering
%         \captionsetup{type=figure,justification=centering}
%         \begin{tikzpicture}[
%                 mycircle/.style={
%                         draw,
%                         circle,
%                         minimum height=.75cm,
%                         minimum width=.75cm
%                     },
%                 mysquare/.style={
%                         draw,
%                         rectangle,
%                         minimum height=.5cm,
%                         minimum width=.5cm,
%                     }
%             ]
%             \node[mysquare] (plus) {+}
%             child {node[mycircle] (epsilon) {\(\varepsilon\)}}
%             child {
%                     node[mysquare] (point) {\(\cdot \)}
%                     child {
%                             node[mysquare] (etoile) {*}
%                             child {
%                                     node[mycircle] (b) {b}
%                                 }
%                         }
%                     child {node[mycircle] (a) {a}}
%                 };

%             \node[right=3mm of a] {\(\{a\}\)};
%             \node[left=3mm of b] {\(\{b\}\)};
%             \node[left=3mm of etoile] {\(\{b^*\}\)};
%             \node[right=3mm of point] {\(\{b^* \cdot a\}\)};
%             \node[left=3mm of epsilon] {\(\{\varepsilon\}\)};
%             \node[above=3mm of plus] {\(\{\varepsilon, b^* \cdot a\}\)};
%         \end{tikzpicture}
%         \caption{
%             Représentation de l'expression régulière à l'aide d'un arbre syntaxique
%         }
%     \end{figure}

% \end{example}

\subsection{Fonction sur les \textit{ER}}

Plusieurs informations sur les expressions régulières nous seront utiles, comme
l'ensemble des premiers/derniers symboles des mots du langage décrit par
l'expression. Il serait aussi intéressant de savoir si son langage contient le
mot vide. Et d'avoir les successeurs des symboles, c'est-à-dire les symboles
suivant un symbole donné.

\vphantom{}

On pourrait calculer individuellement chaque information, mais nous pouvons
calculer tout d'un coup avec une fonction qu'on pourrait appeler \(flnf\). Elle
permet de calculer un tuple contenant toutes ces informations pour une
expression régulière donné.

\vphantom{}

On aurait donc pour une expression régulière \(E\) sur l'alphabet \(\Sigma\),
ceci~:

\begin{center}
    \(flnf(E) = (F, L, \Theta, \delta)\)

    \begin{itemize}
        \item[\textbullet] \(F \subseteq \Sigma\)~: Ensemble des premiers
            symboles de l'expression régulière

            \vphantom{}

        \item[\textbullet] \(L \subseteq \Sigma\)~: Ensemble des derniers
            symboles de l'expression régulière

            \vphantom{}

        \item[\textbullet] \(\Theta\) =
            \(
            \begin{cases}
                \{ \varepsilon \}, & \text{si } \varepsilon \in L(E) \\
                \varnothing        & \text{sinon}
            \end{cases}
            \)

            \vphantom{}

        \item[\textbullet] \(\delta\)~: \(\Sigma \to 2^{\Sigma}\) fonction
            renvoyant les successeurs du symbole donné
    \end{itemize}
\end{center}

La fonction \(flnf\) a donc comme signature~:

\begin{align*}
    flnf: Exp(\Sigma) \to (2^{\Sigma} \times 2^{\Sigma} \times
    \{\varnothing,\{\varepsilon\}\} \times \Sigma \to 2^{\Sigma})
\end{align*}

Et peut-être calculée de cette manière, pour \(E\) et \(G\) des expressions
régulière sur l'alphabet \(\Sigma\) et \(a\) un symbole de \(\Sigma\)~:

\begin{align*}
    flnf(\varepsilon) & = (\varnothing, \varnothing, \varepsilon, \delta) ~|~
    \delta(a) = \varnothing, a \in \Sigma                                     \\
    \vphantom{} \notag                                                        \\
    flnf(a) & = (\{a\}, \{a\}, \varnothing, \delta) ~|~ \delta(a) =
    \varnothing, a \in \Sigma
\end{align*}

\begin{gather*}
    flnf(E + G) = (F \cup F', L \cup L', \Theta \cup \Theta', \delta'')~
    \text{avec} \\
    \delta''(a) = \delta(a) \cup \delta'(a) ~|~ \forall a \in \Sigma \notag \\
    (F, L, \Theta, \delta) = flnf(E) \land (F', L', \Theta', \delta') = flnf(G) \notag
\end{gather*}

\begin{gather*}
    flnf(E \cdot G) = (F'', L'', \Theta \cap \Theta', \delta'')~ \text{avec} \\
    F'' = F \cup F' \cdot \Theta \notag \\
    L'' = L' \cup L \cdot \Theta' \notag \\
    \delta''(a) = \begin{cases} \delta(a) \cup \delta'(a) \cup F', & \text{si}~ a \in L \\ \delta(a) \cup \delta'(a) & \text{sinon}\end{cases} ~|~ \forall a \in \Sigma\notag \\
    (F, L, \Theta, \delta) = flnf(E) \land (F', L', \Theta', \delta') = flnf(G) \notag
\end{gather*}

\begin{gather*}
    flnf(E^*) = (F, L, \{\varepsilon\}, \delta')~ \text{avec} \\
    \delta'(a) = \begin{cases} \delta(a) \cup F, & \text{si}~ a \in L \\ \delta(a) & \text{sinon}\end{cases} ~|~ \forall a \in \Sigma\notag \\
    (F, L, \Theta, \delta) = flnf(E) \notag
\end{gather*}

\begin{remark}
    Il existe un isomorphisme entre les fonctions et les couple antécédents,
    images. Ce qui fait que la fonction des successeurs pourra être représenté
    à l'aide d'un couple.
\end{remark}

\begin{example}
    Prenons par exemple l'expression régulière suivante \(E = a \cdot b + c
    \cdot d\), avec \(E \in Exp(\Sigma)\) et \(\Sigma = \{a, b, c, d\}\).
    Toujours à l'aide d'un arbre syntaxique, on peut calculer ce que
    \(flnf(E)\) donnerait.

    \begin{figure}[H]
        \centering
        \captionsetup{type=figure,justification=centering}
        \begin{tikzpicture}[
                level 1/.style={
                        sibling distance=6cm
                    },
                level 2/.style={
                        sibling distance=3cm
                    },
                mycircle/.style={
                        draw,
                        circle,
                        minimum height=.75cm,
                        minimum width=.75cm
                    },
                mysquare/.style={
                        draw,
                        rectangle,
                        minimum height=.5cm,
                        minimum width=.5cm,
                    }
            ]
            \node[mycircle] (plus) {+}
            child {
                    node[mysquare] (point) {\(\cdot\)}
                    child {node[mycircle] (a) {a} }
                    child {node[mycircle] (b) {b} }
                }
            child {
                    node[mysquare] (point2) {\(\cdot\)}
                    child {node[mycircle] (c) {c} }
                    child {node[mycircle] (d) {d}}
                };

            \node[below=3mm of a] {\((\{a\}, \{a\}, \varnothing, \varnothing)\)};
            \node[below=3mm of b] {\((\{b\}, \{b\}, \varnothing, \varnothing)\)};
            \node[below=3mm of c] {\((\{c\}, \{c\}, \varnothing, \varnothing)\)};
            \node[below=3mm of d] {\((\{d\}, \{d\}, \varnothing, \varnothing)\)};

            \node[left=3mm of point] {\((\{a\}, \{b\}, \varnothing, \{(a, b)\})\)};
            \node[right=3mm of point2] {\((\{c\}, \{d\}, \varnothing, \{(c, d)\})\)};

            \node[above=3mm of plus] {\((\{a, c\}, \{b, d\}, \varnothing, \{(a, b),(c, d)\})\)};
        \end{tikzpicture}
        \caption{
            Représentation de l'expression régulière à l'aide d'un arbre
            syntaxique.
        }
    \end{figure}

    Il advient que \(flnf(E) = \{\{a, c\}, \{b, d\}, \varnothing, \delta\}\)
    avec \(\delta\) qui est défini comme ceci~:

    \begin{align*}
        \delta(a) & = \{b\}       \\
        \delta(b) & = \varnothing \\
        \delta(c) & = \{d\}       \\
        \delta(d) & = \varnothing
    \end{align*}
\end{example}

\vphantom{}

\begin{example}
    Un autre exemple pourrait être \(E' = (a + b) \cdot c^*\), avec cet
    exemple, on voit l'utilité de la parenthèse, car sans elle la concaténation
    aurait été sur \(b \cdot c^*\). Et comme dit précédemment
    (\ref{subsec:parenthese}), son calcul revient à calculer l'expression
    contenue entre les parenthèses.

    \begin{figure}[H]
        \centering
        \captionsetup{type=figure,justification=centering}
        \begin{tikzpicture}[
                level 1/.style={
                        sibling distance=6cm
                    },
                level 2/.style={
                        sibling distance=3cm
                    },
                mycircle/.style={
                        draw,
                        circle,
                        minimum height=.75cm,
                        minimum width=.75cm
                    },
                mysquare/.style={
                        draw,
                        rectangle,
                        minimum height=.5cm,
                        minimum width=.5cm,
                    }
            ]
            \node[mycircle] (point) {\(\cdot\)}
            child {
                    node[mysquare] (plus) {\(+\)}
                    child {node[mycircle] (a) {a} }
                    child {node[mycircle] (b) {b} }
                }
            child {
                    node[mysquare] (etoile) {\(*\)}
                    child {node[mycircle] (c) {c} }
                };

            \node[below=3mm of a] {\((\{a\}, \{a\}, \varnothing, \varnothing)\)};
            \node[below=3mm of b] {\((\{b\}, \{b\}, \varnothing, \varnothing)\)};
            \node[below=3mm of c] {\((\{c\}, \{c\}, \varnothing, \varnothing)\)};

            \node[left=3mm of plus] {\((\{a, b\}, \{a, b\}, \varnothing, \varnothing)\)};
            \node[right=3mm of etoile] {\((\{c\}, \{c\}, \varepsilon, \{(c, c)\})\)};

            \node[above=3mm of point] {\((\{a, b\}, \{a, b, c\}, \varnothing, \{(a, c), (b, c), (c, c)\})\)};
        \end{tikzpicture}
        \caption{
            Représentation de l'expression régulière à l'aide d'un arbre
            syntaxique.
        }
    \end{figure}

    Ce qui fait que \(flnf(E') = (\{a, b\}, \{a, b, c\}, \varnothing,
    \delta')\) avec \(\delta'\) qui est défini comme décrit après~:

    \begin{align*}
        \delta'(a) & = \{c\}       \\
        \delta'(b) & = \{c\}       \\
        \delta'(c) & = \{c\}       \\
        \delta'(d) & = \varnothing
    \end{align*}

\end{example}

\vphantom{}

Une autre fonction qui s'applique aux expressions régulières est
\(linearization\)~; (elle peut paraitre inutile, mais) elle nous servira dans
la Section~\ref{sec:glushkov}. Sa signature est~:

\begin{gather*}
    linearization: Exp(\Sigma) \to Exp(\Sigma \times \mathbb{N}) \\
\end{gather*}

Elle peut être définie de cette manière, pour \(a \in \Sigma\) et \((E, F) \in
(Exp(\Sigma))^2\)~:

\begin{gather*}
    linearization(E) = \pi_2(linearization\_aux(E, 1)) \quad \text{avec}
\end{gather*}

\noindent Avec \(\pi_n\) la fonction de projection sur les tuples et
\(linearization\_aux\) définie récursivement comme ceci~:

\begin{gather*}
    linearization\_aux(\varepsilon, n) = (\varepsilon, n) \\
    linearization\_aux(a, n) = ((a, n), n + 1) \\
    linearization\_aux(E + F, n) = (E' + F', n'') \quad \text{avec} \\
    (E', n') \leftarrow linearization\_aux(E, n) \notag \\
    (F', n'') \leftarrow linearization\_aux(F, n') \notag \\
    linearization\_aux(E \cdot F, n) = (E' \cdot F', n'') \quad \text{avec} \\
    (E', n') \leftarrow linearization\_aux(E, n) \notag \\
    (F', n'') \leftarrow linearization\_aux(F, n') \notag \\
    linearization\_aux(E^*, n) = (E'^*, n') \quad \text{avec} \\
    (E', n') \leftarrow linearization\_aux(E, n) \notag
\end{gather*}

Avec cette définition, on peut voir que tous les symboles sont associés à un
unique entier. Ce qui fait que l'expression régulière résultante ne contient
que des symboles uniques. Et que, de ce fait, si deux couples partagent le même
entier, cela implique qu'ils ont la même valeur de symbole.

\begin{example}

    Si on prend l'expression régulière \(E = \varepsilon + b^* \cdot b\), avec
    \(E \in Exp(\Sigma)\) et \(\Sigma = \{a, b, c, d\}\).

    \begin{figure}[H]
        \centering
        \captionsetup{type=figure,justification=centering}
        \begin{tikzpicture}[
                mycircle/.style={
                        draw,
                        rectangle,
                        rounded corners=.375cm,
                        minimum height=.75cm,
                        minimum width=.75cm
                    },
                mysquare/.style={
                        draw,
                        rectangle,
                        minimum height=.5cm,
                        minimum width=.5cm,
                    }
            ]
            \node[mysquare] (1) {+}
            child {node[mycircle] {\(\varepsilon\)}}
            child {
                    node[mysquare] {\(\cdot \)}
                    child {
                            node[mysquare] {*}
                            child {
                                    node[mycircle] {b}
                                }
                        }
                    child {node[mycircle] {b}}
                };
            \node[mysquare, right=7cm of 1] (2) {+}
            child {node[mycircle] {\(\varepsilon\)}}
            child {
                    node[mysquare] {\(\cdot \)}
                    child {
                            node[mysquare] {*}
                            child {
                                    node[mycircle] {(b, 1)}
                                }
                        }
                    child {node[mycircle] {(b, 2)}}
                };

            \draw[line width=.5mm, -{Stealth[length=5mm, open]}] ($(1.east) + (1.5cm, -2cm)$) -- node[midway, above=2mm] {\(linearization\)} ($(2.west) + (-1.5cm, -2cm)$);
        \end{tikzpicture}
        \caption{
            Représentation à l'aide d'un arbre syntaxique de l'expression régulière
            une fois après avoir fait appel à \(linearization\) sur elle.
        }
    \end{figure}

\end{example}

\vphantom{}

\subsection{Conclusion}

On saisit aisément que ces expressions ont beau être simples (peu d'opération
comparé aux expressions régulières d'\textit{UNIX}). On peut voir qu'elles
permettent de décrire des langages très complexes et en quantité infinie. En
revanche, il est difficile de savoir si un mot est reconnu par une expression
régulière simplement. Par exemple est-ce que le mot \(eipipipipipip\) est
reconnu par cette expression \(((((o \cdot \varepsilon)+(\varepsilon \cdot
e))+((g\cdot \varepsilon) \cdot \varepsilon^*)) \cdot ((\varepsilon \cdot
i)\cdot (p+\varepsilon))^*)\)~? La réponse est oui. C'est pour cela qu'il
serait peut-être intéressant d'utiliser un autre objet pour reconnaitre des
mots, comme les automates que nous allons voir maintenant.
