Le formalisme Relax NG

Jean-François Perrot

  1. Résumé
  2. Utilisation
  3. Exemple-1
  4. Exemple 2
  5. "Expressions de patrons" dans Relax-NG
  6. Combinaison de grammaires en Relax-NG
    1. Deux manières de combiner
    2. Exemple : météo étendue 
    3. Application à la description des menus

  1. Résumé


  2. Utilisation


  3. Exemple-1

    reformulation en RelaxNG de la DTD Nom_note1.dtd :


    <?xml version="1.0" encoding='ISO-8859-1'?>
    <!-- Tableau Noms-Notes par attributs -->
    <!ELEMENT liste (eleve*)>
    <!ELEMENT eleve EMPTY>
    <!ATTLIST eleve nom CDATA #REQUIRED>
    <!ATTLIST eleve note CDATA #REQUIRED>



    En syntaxe compacte : fichier Nom_note1.rnc

    #Nom_note1
    #---------
    start = listétudiants #
    point de départ indispensable
    listétudiants = element liste {étudiant*}
    étudiant = element eleve {nomP, noteSur20}
    nomP = attribute nom { nomPers }
    noteSur20 = attribute note { xsd:integer }
    nomPers = xsd:string { pattern = "[A-Z][a-z]*" }

    #
    on utilise les types de données de XML-schéma.

    Sa forme en syntaxe XML : fichier Nom_note1.rng

    <?xml version="1.0" encoding="UTF-8"?>
    <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
      <!--
        Nom_note1
        - - - - - - - - -
      -->
      <start>
        <ref name="listétudiants"/>
      </start>
      <!-- point de départ indispensable -->
      <define name="listétudiants">
        <element name="liste">
          <zeroOrMore>
            <ref name="étudiant"/>
          </zeroOrMore>
        </element>
      </define>
      <define name="étudiant">
        <element name="eleve">
          <ref name="nomP"/>
          <ref name="noteSur20"/>
        </element>
      </define>
      <define name="nomP">
        <attribute name="nom">
          <ref name="nomPers"/>
        </attribute>
      </define>
      <define name="noteSur20">
        <attribute name="note">
          <data type="integer"/>
        </attribute>
      </define>
      <define name="nomPers">
        <data type="string">
          <param name="pattern">[A-Z][a-z]*</param>
        </data>
      </define>
    </grammar>



    Sa traduction en XML-schéma : fichier Nom_note1.xsd

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
      <!--
        Nom_note1
        - - - - - - - - -
      -->
      <!-- point de départ indispensable -->
      <xs:element name="liste">
        <xs:complexType>
          <xs:sequence>
            <xs:element minOccurs="0" maxOccurs="unbounded" ref="eleve"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="eleve">
        <xs:complexType>
          <xs:attributeGroup ref="nomP"/>
          <xs:attributeGroup ref="noteSur20"/>
        </xs:complexType>
      </xs:element>
      <xs:attributeGroup name="nomP">
        <xs:attribute name="nom" use="required" type="nomPers"/>
      </xs:attributeGroup>
      <xs:attributeGroup name="noteSur20">
        <xs:attribute name="note" use="required" type="xs:integer"/>
      </xs:attributeGroup>
      <xs:simpleType name="nomPers">
        <xs:restriction base="xs:string">
          <xs:pattern value="[A-Z][a-z]*"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:schema>



  4. Exemple 2

    L'exemple météo (J. Malenfant) : fichier meteo1.rnc  - fichier-instance obs.xml

    default namespace = "http://www.malenfant.fr/meteo3"
    start = meteo
    meteo = element meteo { obs+ } # plusieurs observations
    obs = element obs {
       attribute num { text },
       element loc { element nom { text } },
       element moment { text },
       element temp { attribute unit { text }, text },
       element hygro { text },
       element nebulo { text },
       element anemo { text },
       element pluvio { text }, 
       element message { attribute langue { text }?, text }?
       # message et langue optionnels
    }


    version "linéarisée" : fichier meteo2.rnc - fichier-instance idem obs.xml

    default namespace = "http://www.malenfant.fr/meteo3"
    start = meteo
    meteo = element meteo { obs+ }
    obs = element obs { identification, mesures, message? }
    identification = attribute num { text }, loc, moment
    loc = element loc { nom }
    nom = element nom { text }
    moment = element moment { text }
    mesures = temp, hygro, nebulo, anemo, pluvio
    temp = element temp { attribute unit { text }, text }
    hygro = element hygro { text }
    nebulo = element nebulo { text }
    anemo = element anemo { text }
    pluvio = element pluvio { text }
    message = element message { langue?, text }
    langue = attribute langue { text }



    version perfectionnée : fichier meteoP.rnc - fichier-instance idem obsP.xml

    default namespace = "http://www.malenfant.fr/meteo3"
    start = meteo
    meteo = element meteo { obs+ }
    obs = element obs {
        identification, mesures, message?
    }
    identification =
        attribute num {
            xsd:NMTOKEN {
                pattern = "[A-Z]{2,2}\d+V\d{3,3}"
            }
        },
        loc, moment
    loc = element loc { localisation }
    localisation = element nom { text }
    moment = element moment { xsd:dateTime }
    mesures =
        (attribute type { xsd:NMTOKEN "mobile" },
            temp, anemo)
        |
        (attribute type { xsd:NMTOKEN "fixe" },
            temp, hygro, nebulo, anemo, pluvio)
    temp = element temp {
        attribute unit { "celsius" | "farenheit" },
        xsd:decimal
    }
    hygro = element hygro { xsd:decimal }
    nebulo = element nebulo { xsd:decimal }
    anemo = element anemo { xsd:decimal }
    pluvio = element pluvio { xsd:decimal }
    message = element message { langue?, string }
    langue = attribute langue { xsd:language }


  5. "Expressions de patrons" dans Relax-NG


    Toute la supériorité de Relax-NG réside dans la notion (apparemment cachée) d'expression à valeur de pattern,
    qui permet d'écrire sans se tromper en utilisant les mécanismes habituels de l'écriture algébrique :
    peut-on parler de "transparence référentielle" ?

    Exemple tiré de meteoP.rnc

        (attribute type { "mobile" }, temp, anemo)
        |
        (attribute type { "fixe" },temp, hygro, nebulo, anemo, pluvio)



    Un "patron" (pattern) est bel et bien la valeur d'une expression, dont le langage comporte :

    Ex : Considérons la définition d’un nœud dans un arbre binaire. Un nœud
    binaire peut contenir soit deux nœuds fils, soit une feuille de contenu vide :
        nœud = element nœud {
            (nœud, nœud) | feuille { empty }
        }

  6. Combinaison de grammaires en Relax-NG

    1. Deux manières de combiner

      Différencce entre
      • la "référence exerne" par "external <URL>"
      • et l'inclusion de grammaire par "include <URL>"

      La référence externe délègue une partie du filtrage à la grammaire référencée
      l'inclusion importe toute la mécanique (y compris les sous-structures) pour servir dans la grammaire incluante.

    2. Exemple : météo étendue 

      On introduit une notion de coordonnées pour repérer les observations mobiles - fichier-instance obsC.xml

      fichier coordonnees.rnc
      coordonnees = element coordonnees {
         element latitude {
             attribute direction { xsd:NMTOKEN "nord" | xsd:NMTOKEN "sud" },
             position },
        element longitude {
             attribute direction { xsd:NMTOKEN "est" | xsd:NMTOKEN "ouest" },
             position
        }
      }
      position =
         element d { xsd:unsignedShort },
         element m { xsd:unsignedShort },
         element s { xsd:unsignedShort }


      fichier meteoC.rnc

      include "coordonnees.rnc" # inclusion sans rédéfinition

      include "meteo3.rnc" { meteo = element meteo { intitules?, obs+ } }
      # inclusion avec redéfinition - contient l'élément start

      intitules = element intitules { # pour produire des tableaux
         langue?,
         (element int-loc { text } & element int-moment { text } &
          element int-temp { text } & element int-hygro { text } &
          element int-nebulo { text } & element int-anemo { text } &
          element int-pluvio { text } & element int-message { text })
      }
      localisation |= coordonnees #
      combinaison par choix


    3. Application à la description des menus

      Les menus en question ont déjà été vus
      On souhaite en donner une description aussi précise que possible...

      1. Menus simples, sans prix - fichier menu.rnc, exemple menu.xml

        start = menu
        menu = element liste { apéritif, entrée, plat, dessert, fin }
        suite =  annonce, choix*
        annonce = element annonce { text }
        choix = element choix { text }
        apéritif = element étape { attribute nom { "apéritif" }, suite}
        entrée = element étape { attribute nom { "entrée" }, suite}
        plat = element étape { attribute nom { "plat" }, suite}
        dessert = element étape { attribute nom { "dessert" }, suite}
        fin = element étape { attribute nom { "fin" }, annonce}



      2. Menus avec prix - fichier menuP.rnc, exemple menuP.xml 
        On souhaite exprimer le plus simplement possible qu'un menu avec pris, c'est tout bonnement un menu
        dont les choix portent un attribut "prix".

        include "menu.rnc" {choix = element choix {prix & text }}

        prix = attribute prix { xsd:unsignedShort }