Ecriture d'une grammaire MOïRA
MOïRA est une méthode et un outil destinés à intégrer de façon optimale le traitement des données textuelles dans une application.

Cette page décrit la façon d'écrire une grammaire selon le formalisme MOïRA.

Contact :    alain.buhsing@free.fr


o Contenu de cette page :
 

oLe formalisme BNF standard
oLe formalisme BNF étendu de Moïra
oAnalyse syntaxique. Grammaire déterministe

Retour à MOïRA



o Le Formalisme B.N.F standard

MOïRA utilise un mode de description proche du modèle grammatical traditionnel BNF (Backus-Naur Form).
Le formalisme BNF a été conçu à la fin des années 50 par John Backus pour définir rigoureusement le langage ALGOL, ancêtre des langages structurés.

Comme pour une langue naturelle, la syntaxe d'un langage formel est définie par une grammaire Une grammaire est un mécanisme fini capable de produire toutes les phrases d'un langage et seulement celles-là.

Une grammaire est constituée d'une liste de productions décrivant les règles grammaticales applicables au vocabulaire de ce langage. Le vocabulaire est constitué de deux types de symboles :

Chaque non-terminal est défini par une production, elle même constituée une ou plusieurs règles syntaxiques.
Une règle comporte une partie gauche et une partie droite séparées par le métasymbole de dérivation ::= Il se lit "se compose de".La partie gauche d'une production est constituée d'un non-terminal. La partie droite est constituée d'une liste éventuellement vide de non-terminaux et/ou de terminaux.

Par exemple, la règle suivante : "Une phrase type se compose d'un sujet, d'un verbe et d'un complément et se termine par un point", s'écrit en BNF :
 

       <phrase type> ::= <sujet> <verbe> <complément> •

Un test en C posséde 2 syntaxes : une syntaxe simple et une syntaxe complète avec  bloc else.
 

      <test> ::= if  ( <condition> <instruction> ;
      <test> ::= if  ( <condition> <instruction> ; else <instruction> ;

Le formalisme BNF standard pour décrire des listes d'instructions est assez lourd :
 

      <bloc_C>           ::= { <des_instructions> }
      <des_instructions> ::= <des_instructions>  ; <instruction>
      <des_instructions> ::= <instruction>
      <des_instructions> ::=

Pour cette raison, on a dû améliorer le formalisme pour aboutir au BNF étendu.

Retour au début



o Le formalisme BNF étendu de MOïRA

On a pu remarquer la lourdeur de certaines productions : Si l'on doit définir la totalité d'un langage par une grammaire, on aimerait disposer d'une plus grande concision et d'une plus grande lisibilité.

On considère les réels, entiers, identifieurs comme des terminaux du langage, terminaux ayant une forme variable mais bien définie. On nomme ces constructions les expressions régulières lexicales du langage. On les note au moyen d'un identifieur précédé du métacaractère % : %entier, %reel,%identifieur et %chaine.

 L'alternative symbolysée par une barre | (prononcer "ou")  qui permet de regrouper les règles ayant même partie gauche dans une seule production.
 

               <numérique> ::= %entier | réel

L'itération ou la liste qui existe en deux types : l'itération 0 à n symbolysée par { }* et l'itération 1 à n symbolysée par { }+ . On rangera aussi dans cette catégorie la phase facultative notée entre crochets  [ ] .
 

      <zéro_ou_plusieurs_trucs> ::= { <truc > }*
      <un_ou_plusieurs_trucs>   ::= { <truc > }+
      <truc_facultatif>         ::= [ <truc > ]

Les listes font souvent apparaître des séparateurs. Par exemple le point-virgule séparateur d'instructions  ou la virgule séparatrice des indices d'un tableau. MOïRA permet d'accoler au méta-caractère d'itération ( le "*" ou le "+") le séparateur de la liste.
 

      <séparés par une virgule> ::= { <truc> }+,

Enfin, on peut aussi faire disparaître les métacaractères < >, à condition de prévoir un mécanisme permettant de distinguer les mots réservés et les non-terminaux. Sur un document on peut souligner ou afficher les mots-clés en caractères gras. Certains constructeurs syntaxiques (par exemple YACC) imposent de déclarer préalablement les mots réservés. La solution de MOïRA consiste à adopter les majuscules pour les mots réservés et les minuscules pour les non-terminaux. Notons que cette convention ne concerne que la grammaire  et pas le texte analysé.
 

      bloc_while ::= WHILE "(" condition ")"  instruction 

En résumé :
 

L'alternative type_numérique ::= entier | reel
Les listes un_ou_plusieurs_trucs   ::= { truc }+
zéro_ou_plusieurs_trucs ::= { truc }*
séparés par une virgule ::= { truc }+,
Elément facultatif (notation Moïra) truc_facultatif   ::=  { truc }
Elément facultatif ( notation standard) truc_facultatif   ::=  [ truc ]
Mots clés en majuscules  IF "("  condition ")" instruction 
Séparateurs entre quotes  tableau ::= identifieur "[" expression "]"
Expressions régulières lexicales %entier %reel %identifieur  %chaine

Les notations en italique sont des spécificités de MOïRA.

Retour au début



oAnalyse syntaxique et grammaire déterministe

L'analyse syntaxique consiste à vérifier si un texte est conforme à la grammaire utilisée pour spécifier ce texte.
Prenons une grammaire décrivant des expressions arithmétiques.
 

expression  ::= expression oper expression | var
oper        ::= "+" | "-" | "*" | "/"
var         ::= parentheses | %IDENT | %REEL
parentheses ::= "(" expression ")"

Soit le texte :

          A + B * C

On dit qu'une phrase est conforme à la grammaire si on peut retrouver l'ensemble des règles qui ont abouti à cette phrase en partant de l'axiome. On peut représenter ces dérivations de façon arborescente :
 

Arbre de dérivation

La racine de l'arbre de dérivation est l'axiome (première règle de la grammaire), ses feuilles sont des terminaux.

En suivant les règles de la grammaire fournie sur le dernier exemple, on peut remarquer que cette représentation arborescente n'est pas unique : Comme la première règle de décomposition d'une expression est symétrique, on peut tout à fait décomposer l'expression située à gauche, soit celle située à droite de l'opérateur. Il en ressort que l'arbre syntaxique associé à la phrase "A+B*C" selon cette grammaire n'est pas unique.  On dit alors que la grammaire est ambigüe :
 
 

Arbre obtenu en dérivant l'élément récursif à gauche de la règle :  Arbre obtenu en dérivant l'élément récursif à droite de la règle
express ::= express oper var | var express ::= var oper express | var

Une grammaire ambigüe met  l'analyeur syntaxique dans une situation imposssible. En effet un traitement algoritmique se doit d'être déterministe pour d'effectuer un traitement sémantique correct. Dans le cas contraire, la sémantique risque  d'être aléatoire.

Comment lever une telle ambigüité ?. Dans ce cas précis l'ambigüité provient d'une récursivité double, elle est donc levée si l'on choisit de faire apparaître la récursivité à gauche ou la récursivité à droite.

Prenons une grammaire récursive à gauche :
 

expression  ::= expression oper var | var
oper        ::= "+" | "-" | "*" | "/"
var         ::= parentheses | %IDENT | %REEL
parentheses ::= "(" expression ")"

Dans ce cas, l'arbre de dérivation est unique, puisque l'on ne peut dériver que l'expression de gauche :
 

On pourrait fort bien imaginer une forme récursive à droite. Par exemple :
 

    expression ::= var oper expression | var

En fait la récursivité à gauche est bien plus intéressante du point de vue formel que la récursivité à droite, car elle correspond au sens conventionnel des calculs : la partie gauche d'une opération correspond au total déja calculé, et la partie droite à l'élément en cours de calcul. Autre exemple : la phrase A-B-C doit être sémantiquement équivalente à (A-B)-C et non à l'expression A-(B-C). Or c'est cette seconde interprétation qui serait fournie par une grammaire récursive à droite.



    Priorité des opérateurs

On remarque que tous les opérateurs étant décrits au même niveau, c'est le premier rencontré sur la phrase à analyser qui décide de l'ordre des opérations. Si l'on évalue l'expression selon l'ordre des opérations établi par cet arbre de dérivation, on voit que l'on aboutit à un résultat ne correspondant pas aux priorités classiques d'opérateurs : en effet A+B*C est conventionnellement reconnu comme équivalent à A+(B*C) et non à (A + B) * C.
Si l'on désire évaluer l'expression, on a donc deux possibilités :

expression     ::= addition | soustraction | terme

addition       ::= expression "+" terme
Soustraction   ::= expression "-" terme

terme          ::= multiplication | division | facteur

multiplication ::= terme "*" facteur
division       ::= terme "/" facteur

facteur        ::= parentheses | identifieur | reel

parentheses    ::= "(" expression ")"
identifieur    ::= %IDENT
reel           ::= %REEL

Ainsi avec cette dernière grammaire le texte :
 

          A + B * C

est structuré de façon cohérente avec l'ordre des calculs :
 

On voit  que, pour un langage donné, il existe plusieurs grammaires possibles. Le fait de faire apparaître plusieurs priorités d'opérateurs, ou de définir une grammaire non ambigüe, peut ressembler superflu au départ, mais ce travail permet d'une part d'utiliser les outils d'analyse syntaxique existant, et surtout de simplifier le travail sémantique qui va suivre.

Retour au début