Cours n° 10, 8 décembre 2011

Jean-François Perrot

Mise en œuvre par programme 2

  1. Mise en œuvre de la fonction d'analyse
    1. Mise en œuvre minimale : accepter ou refuser une chaîne-candidate
    2. Mise en œuvre plus classique "match":
    3. "matchAll" : Trouver toutes les sous-chaînes maximales...

  2. Syntaxes étendues pour les e.r.
    1. la grammaire étendue
    2. les extensions de type LEX
      1. La notation des ensembles de caractères entre crochets
      2. Les caractères spéciaux '\n', '\r' et '\t' :
      3. La notation du nombre de répétitions entre accolades

  3. L'exemple des nombres en décimal, octal et hexadécimal
    1. Expression régulière
    2. Automate
    3. Fonction C++ traduisant l'automate minimal

Mise en œuvre de la fonction d'analyse

Nous avons vu la manière la plus simple de mettre en œuvre effectivement la fonction booléenne traduisant un automtate fini.
Nous allons à présent examiner deux procédés qui sont en usage courant (nous les retrouverons en Perl).

  1. Mise en œuvre minimale : accepter ou refuser une chaîne-candidate

    Cette fois-ci, en traitant plusieurs chaînes à la suite, sans devoir relancer le programme à chaque fois.
    fichier accept_bcl.cpp

    #include "../automates/auto.h" 
    #include <iostream>
    #include <string>
    using namespace std;

    int main () {
    cout << "Bonjour ! \nJe suis le programme de reconnaissance \nSortie par CTRL-D \n";
    while(true){
    string lu;
    cout << "Donnez une chaîne\n";
    if (!getline(cin, lu)){
    cout << "Au-revoir !\n";
    break; // sortie de la boucle, fin de l'exécution
    }
    // on a lu une chaîne...
    int start = 0;
    if( analyser(lu, start) ){
    cout << "Oui\n";
    }else{
    cout << "Non\n";
    }
    }//while
    return 0;
    }


  2. Mise en œuvre plus classique "match":

    trouver une sous-chaîne de la chaîne-candidate qui fait partie du langage
    (implémentation "par force brutale", sans souci d'efficacité).

    fichier match_sg.cpp

    // La première sous-chaîne, la plus longue

    #include "../automates/auto.h"
    #include <iostream>
    #include <string>
    using namespace std;

    int main () {
    cout << "Bonjour ! \nJe suis le programme de 'match'\n";
    string lu;
    cout << "Donnez une chaîne\n";
    getline(cin, lu);

    int k = lu.length();
    int i; // indice de début de la sous-chaîne à tester

    for( i = 0; i< k; i++ ){ // boucle extérieure
    //cout << i << "\n";
    int j; // indice de fin de la sous-chaîne à tester
    for( j = k; j >= i; j-- ){ // boucle intérieure
    string cand = lu.substr(i, j-i);
    //cout << cand << "\n";
    int start = 0;
    if( analyser(cand, start) ){
    cout << cand <<"\n";
    break; // sort de la boucle intérieure
    }
    } // for intérieur
    if( j >= i ) break; // on est sorti de la boucle avant la fin,
    // c'est donc qu'on a trouvé, et on sort
    } // for extérieur
    if( i == k ) cout << "no match\n";

    return 0;
    }


  3. "matchAll" : Trouver toutes les sous-chaînes maximales...

    fichier matchAll.cpp
    à utiliser avec redirection d'un fichier en entrée, par
    %./matchAll < try.txt



    // Toutes les plus longues sous-chaînes

    #include "../automates/auto.h"
    #include <iostream>
    #include <string>
    using namespace std;

    int main () {

    string lu;
    getline(cin, lu, '\0'); // censé être un fichier par redirection
    int k = lu.length();

    int i;
    for( i = 0; i< k; i++ ){ // boucle extérieure
    int j;
    for( j = k; j > i; j-- ){ // boucle intérieure
    string cand = lu.substr(i, j-i);
    int start = 0;
    if( analyser(cand, start) ){
    cout << cand <<"\n";
    break; // sort de la boucle intérieure
    }
    }// for intérieur
    if( j > i ) i = j; // et on repart

    } // for extérieur

    return 0;
    }

Syntaxes étendues pour les e.r.


Au-delà de la syntaxe de base où seuls les trois opérateurs prévus par la définition (et l'opérateur "+") sont autorisés,
le logiciel automate propose deux extensions
[voir dans la fenêtre d'aide, sous la rubrique Expressions régulières > définition,
où la syntaxe de base est décrite sous le nom de grammaire standard].
  1. la grammaire étendue

    qui exploite les propriétés de fermeture de la classe des langages réguliers
    par intersection ensembliste et par passage au complémentaire.
    Ces propriétés sont la conséquence directe de
    1. l'égalité entre la classe des langages réguliers et celle des langages reconnaissables par automates finis
      (théorème de Kleene, cours n° 6)
    2. la fermeture par intersection ensembliste et par passage au complémentaire de la classe des langages reconnaissables par automates finis (cours n° 5).

    Cette grammaire autorise donc trois opérateurs suppémentaires :
    1. l'esperluette "&" binaire notant l'intersection
    2. le point d'exclamation "!" unaire postfixé notant le passage au complémentaire
    3. le "moins" "-" binaire notant la différence E-F = E∩∁F
      c'est-à-dire, en employant la syntaxe étendue : E-F = E&F!

  2. les extensions de type LEX

    Lex est un célèbre générateur d'analyseurs lexicaux des années '70, qui emploie les e.r. d'une manière essentielle.
    Nous n'utiliserons pour l'instant que trois de ces extensions, qui serviront abondamment dans la suite.
    1. La notation des ensembles de caractères entre crochets
      • Une suite de caractères entre crochets désigne l'ensemble de ces caractères
        [abc] = (a|b|c)

      • Deux caractères séparés par un tiret entre crochets désignent l'intervalle entre ces caractères
        (en tant qu'ensemble de caractères), au sens de l'énumération du code ASCII
        [0-9], [a-z], [A-Z], [a-zA-Z]
        les chiffres, les lettres minuscules, les majuscules, toutes les lettres  (maj. et min.)

      • Un chapeau "^" entre crochets désigne tout sauf ce qui suit (le complémentaire)
        [^0-9] = tout sauf les chiffres

      Exemples :
      • Les entiers en notation décimale = rien que des chiffres, et contenant au moins un chiffre = [0-9]+
      • Les identificateurs standard = des lettres ou des chiffres, commençant par une lettre = [a-zA-Z][a-zA-Z0-9]*
      • Les commentaires à la Pascal = commençant par une accolade ouvrante,
        se terminant à la première accolade fermante (commentaires non emboîtés) = \{[^\}]*\}
        (il faut écrire "\{"  et "\}" en raison du rôle des accolades dans la syntaxe, voir plus loin ;
        on aurait aussi pu écrire "[{]" et "[}]").
      • Les commentaires à la C, que nous avons vus au cours 4 : en lisant l'automate, on aboutit à l'e.r.
        /[*]([^/*]|[/]|[*]+[^/*])*[*]+/
        (il est indispensable d'écrire "[*]" pour signifier le caractère étoile, vu son rôle dans la syntaxe des e.r.,
        on aurait aussi pu écrire "\*").

    2. Les caractères spéciaux '\n', '\r' et '\t' :
      connus respectivement comme saut de ligne (ASCII x0A), retour-chariot (ASCII x0D) et tabulation horizontale (ASCII x09).
      Ces trois caractères ont en commun de ne pas pouvoir être cités sans déranger le texte qui les cite,
      à la différence de l'espace, alias caractère blanc ' ' qu'on peut parfaitement mentionner sans dommage.
      Aussi emploie-t-on couramment les trois formes ci-dessus pour les désigner, notamment dans les expressions régulières,
      où ils apparaissent très fréquemment (c'est le moment de réviser vos connaissances sur la réalisation du saut de ligne
      suivant les différents systèmes d'exploitation).

      Ainsi l'e.r. "[ \t]" désigne l'ensemble {espace, tabulation} souvent employé pour séparer les mots.
      et "[ \n\t]" = le blanc, le saut de ligne, la tabulation idem.
      Les commentaires en fin de ligne = commençant par deux obliques,
      se terminant à la première fin de ligne : //[^\n]*\n
    3. La notation du nombre de répétitions entre accolades
      Avec les mêmes conventions de priorité que '*' et '+', le nombre de répétitions d'un motif m s'écrit entre accolades
      m{k} = mm...m (k fois)
      et m{h, k} = {mm...m (i fois) | h <= i <= k}.

      Ainsi l'e.r. "[0-9]{2}" désigne toutes les suites de deux chiffres décimaux.

L'exemple des nombres en décimal, octal et hexadécimal

Essayez-la avec le cadre matchAll !