XML en Prolog

Notes sur la bibliothèque SWI-Prolog SGML/XML parser

Transsformation de documents XML (fichiers) en termes Prolog.
  1. fichier -> terme (loading)
  2. terme -> fichier (writing)
  3. vérification qu'un terme donné est bien dans la forme requise (checking)
  1. Loading Structured Documents
    1. Principe
    2. Précisions
      1. Liste
      2. Rôle de la DTD
      3. En l'absence de DTD
      4. Réglage de l'imprimeur
      5. Exercice

  2. Writing documents

  3. Type checking

  1. Loading Structured Documents

    library(sgml)

    Prédicats load_structure et load_xml_file
    1. Principe

      Chaque element XML est représenté par un terme Prolog ainsi construit :
      • le foncteur est  element, ternaire
      • arg. 1 = le nom de la balise, en tant qu'atome
      • arg. 2 = la liste des attributs [attr1=val1, attr2=val2,...]
        • les noms d'attributs et les valeurs représentés par des atomes
        • les namespaces traités comme des attributs ordinaires de nom xmlns
      • arg. 3 = la liste des fils,
        • y compris les nœuds texte sous forme d'atomes

      Exemple (artificiel mais complet !) :
      XML :
      <Ex:Balise xmlns:Ex="http://www.epita.com" but="illustration" modalité="sans problème">
          sans autre
          <Ex:suite-1> cela reste à voir </Ex:suite-1>
          forme de
          <Ex:suite2 />
          procès
      </Ex:Balise> 

      Prolog : element(
               'Ex:Balise',
               ['xmlns:Ex'='http://www.epita.com', but=illustration, modalité='sans problème'],
               ['sans autre',
                element('Ex:suite-1', [], ['cela reste à voir']),
                'forme de',
                element('Ex:suite2', [], []),
                procès
               ])


      On note la présence de simple quotes autour des atomes dès que
      • ils commencent par une majuscule (réservé aux variables logiques)
      • ou qu'ils contiennent un caractère non-alphanumérique,
      • les lettres accentuées ne posent point de problème, Prolog travaille en UTF-8.
    2. Précisions

      1. Liste
        Attention ! Le fichier tout entier est traduit par une liste contenant les instr. de traitement et l'élément racine !
      2. Rôle de la DTD
        En l'absence de DTD, load_xml_file(NomFich, Terme) conserve comme significatifs
        tous les blancs, les tabs et les sauts de ligne (normal vu les principes d'XML).
        Exemple, avec un fichier XML reproduisant exactement les indentations de l'exemple artificiel ci-dessus
        [fichier Ex.xml]

        ?- use_module(library(sgml)).
        ?- load_xml_file('Ex.xml', Terme).
        Terme = [element('Ex:Balise', ['xmlns:Ex'='http://www.epita.com', but=illustration, modalité='sans problème'], ['\n\tsans autre\n\t', element('Ex:suite-1', [], [' cela reste à voir ']), '\n\tforme de\n\t', element('Ex:suite2', [], []), '\n\tprocès\n'])].



        Mais en présence d'une DTD le même prédicat l'utilise pour ne conserver que la structure spécifiée,
        en rectifiant au passage des erreurs mineures.
        Exemple avec les fichiers Voiture.xml et Voiture.dtd :

        ?- load_xml_file('Voiture.xml', Terme).
        Warning: SGML2PL(xml): Voiture.xml:8: Ignored end-tag for "Cylindres" which is not open
        Terme = [element('Voiture', [marque='Renault', modèle='Safrane'], [element('Carosserie', [couleur=rouge], [element('Capot', [], ['Un peu cabossé'])]), element('Moteur', [], [element('Cylindres', [], []), element('Allumage', [], ['Défectueux'])]), element('Transmission', [type=automatique, nb_vitesses='5'], [element('Boîte', [], []), element('TrainAV', [], []), element(..., ..., ...)])])].



      3. En l'absence de DTD
        Nous travaillerons sur des fichiers XML dépourvus de DTD (provenant du site Jodhpur),
        le contrôle structurel de ces fichers étant confié à des schémas XML.
        Pour éliminer les blancs, tabs et sauts de ligne "parasites", nous utiliserons de préférence
        load_structure(NomFich, Terme, [dialect(xml),space(remove)]).

        Exemple avec le fichier-source qrd.xml  :

        ?- load_structure('qrd.xml', XmlSource, [dialect(xml),space(remove)]).
        XmlSource = [element('Prog', [], [element('Variables', [], [element('Var', [name=a], []), element('Var', [name=b], []), element('Var', [... = ...], []), element('Var', [...], []), element(..., ..., ...)|...]), element('Main', [], [element('Read', [], [element('Var', [...], [])]), element('Read', [], [element(..., ..., ...)]), element('Assignment', [], [...|...]), element(..., ..., ...)|...])])].



      4. Réglage de l'imprimeur
        Pour ne pas nous encombrer avec un terme de grande taille,
        l'imprimeur de Prolog pratique l'ellipse ("...") des termes de profondeur dépassant 5,
        ce qui nous empêche de lire notre terme au complet.

        Pour l'avoir tout entier, il convient de modifier  les réglages de l'imprimeur, en demandant : set_prolog_flag(toplevel_print_options,[quoted(true), portray(true), max_depth(0), attributes(portray)]).


        ?- set_prolog_flag(toplevel_print_options,[quoted(true), portray(true), max_depth(0), attributes(portray)]).
        true.
        ?-  load_structure('qrd.xml', XmlSource, [dialect(xml),space(remove)]).
        XmlSource=[element('Prog',[],[element('Variables',[],[element('Var',[name=a],[]),element('Var',[name=b],[]),element('Var',[name=t],[]),element('Var',[name=q],[]),element('Var',[name=r],[]),element('Var',[name=d],[])]),element('Main',[],[element('Read',[],[element('Var',[name=a],[])]),element('Read',[],[element('Var',[name=b],[])]),element('Assignment',[],[element('Var',[name=t],[]),element('VarExp',[],[element('Var',[name=b],[])])]),element('Loop',[],[element('Test',[op= <=],[element('Bin',[op= (-)],[element('VarExp',[],[element('Var',[name=t],[])]),element('VarExp',[],[element('Var',[name=a],[])])]),element('Cst',[val='0'],[])]),element('Assignment',[],[element('Var',[name=t],[]),element('Bin',[op= (*)],[element('Cst',[val='2'],[]),element('VarExp',[],[element('Var',[name=t],[])])])])]),element('Assignment',[],[element('Var',[name=q],[]),element('Cst',[val='0'],[])]),element('Assignment',[],[element('Var',[name=r],[]),element('VarExp',[],[element('Var',[name=a],[])])]),element('Assignment',[],[element('Var',[name=d],[]),element('VarExp',[],[element('Var',[name=t],[])])]),element('Loop',[],[element('Test',[op= (>)],[element('Cst',[val='0'],[]),element('Bin',[op= (-)],[element('VarExp',[],[element('Var',[name=b],[])]),element('VarExp',[],[element('Var',[name=d],[])])])]),element('Sequence',[],[element('Assignment',[],[element('Var',[name=d],[]),element('Bin',[op= (/)],[element('VarExp',[],[element('Var',[name=d],[])]),element('Cst',[val='2'],[])])]),element('Conditional',[],[element('Test',[op= (>=)],[element('Bin',[op= (-)],[element('VarExp',[],[element('Var',[name=r],[])]),element('VarExp',[],[element('Var',[name=d],[])])]),element('Cst',[val='0'],[])]),element('Sequence',[],[element('Assignment',[],[element('Var',[name=r],[]),element('Bin',[op= (-)],[element('VarExp',[],[element('Var',[name=r],[])]),element('VarExp',[],[element('Var',[name=d],[])])])]),element('Assignment',[],[element('Var',[name=q],[]),element('Bin',[op= (+)],[element('Bin',[op= (*)],[element('Cst',[val='2'],[]),element('VarExp',[],[element('Var',[name=q],[])])]),element('Cst',[val='1'],[])])])]),element('Assignment',[],[element('Var',[name=q],[]),element('Bin',[op= (*)],[element('Cst',[val='2'],[]),element('VarExp',[],[element('Var',[name=q],[])])])])])])]),element('Write',[],[element('VarExp',[],[element('Var',[name=q],[])])]),element('Space',[],[]),element('Write',[],[element('VarExp',[],[element('Var',[name=r],[])])]),element('Newline',[],[])])])].


      5. Exercice
        Nous avons commencé en cours l'écriture et le test d'un vérificateur structurel pour les termes issus de fichiers XML représentant des programmes en syntaxe abstraite de la même farine que l'exemple précédent.
        fichier checkProg.pl

        Mener cette écriture à son terme.
  2. Writing documents

    library(sgml_write)

    xml_write(+Stream, +Term, +Options)
    Write the XML header with encoding information and the content of the document as represented by Term to Stream. This predicate deals with XML with or without namespaces. If namespace identifiers are not provided they are generated.

    Ex. avec liste d'options vide
    :- use_module(library(sgml_write)).
    ecrire_terme(NomFich, T) :- open(NomFich, write, Str), xml_write(Str, T, []), close(Str).

  3. Type checking

    library(sgml)

    xml_is_dom(@Term)
    True if Term is an SGML/XML term as produced by one of the above predicates and acceptable by xml_write/3 and friends.