<?xml version="1.0" encoding="utf-8"?>

<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<yield id="chapter">

  <p>Les <strong>compilateurs</strong> permettent d'<strong>analyser</strong> et
  <strong>manipuler</strong> des données <strong>textuelles</strong>. Leurs
  applications sont très nombreuses. <code>Hoa\Compiler</code> propose de
  manipuler plusieurs compilateurs selon les besoins.</p>

  <h2 id="Table_of_contents">Table des matières</h2>

  <tableofcontents id="main-toc" />

  <h2 id="Introduction" for="main-toc">Introduction</h2>

  <blockquote cite="https://fr.wikipedia.org/wiki/Nicolas_Boileau">Ce qui se
  conçoit bien s'énonce clairement, et les mots pour le dire viennent
  aisément.</blockquote>
  <p>Un <strong>langage</strong> est une façon d'exprimer ou de
  <strong>formuler</strong> une <strong>solution</strong> à un
  <strong>problème</strong>. Et des problèmes, il en existe beaucoup. Nous
  lisons et écrivons dans plusieurs langages au quotidien, et certains de ces
  langages sont <strong>compris</strong> par des <strong>machines</strong>.
  Cette opération est possible grâce aux <strong>compilateurs</strong>.</p>
  <p>La <a href="https://fr.wikipedia.org/wiki/Théorie_des_langages">théorie des
  langages</a> étudie entre autres l'<strong>analyse automatique</strong> de ces
  langages à travers des outils comme des <strong>automates</strong> ou des
  <strong>grammaires</strong>. Il est nécessaire d'avoir un cours détaillé pour
  bien comprendre tous ces concepts. Toutefois, nous allons essayer de
  vulgariser un minimum pour permettre une utilisation correcte de
  <code>Hoa\Compiler</code>.</p>

  <h3 id="Language_and_grammar" for="main-toc">Langage et grammaire</h3>

  <p>Un <strong>langage</strong> est un ensemble de <strong>mots</strong>.
  Chaque mot est une <strong>séquence</strong> de <strong>symboles</strong>
  appartenant à un <strong>alphabet</strong>. Un symbole représente la plus
  petite <strong>unité lexicale</strong> d'un langage, il est atomique et nous
  l'appellons <strong>lexème</strong> (ou <em lang="en">token</em> en anglais).
  Les séquences de lexèmes représentant les mots sont construites avec des
  <strong>règles</strong>. À partir d'un mot et d'une règle racine, nous allons
  essayer de <strong>dériver</strong> ses sous-règles. Si une dérivation existe,
  alors le mot est considéré comme <strong>valide</strong>, sinon il est
  considéré comme <strong>invalide</strong>. Nous parlons aussi de
  <strong>reconnaissance</strong> de mots. Par exemple, si nous considérons les
  règles suivantes :</p>
  <pre><code>    exp ::= exp + exp
          | nombre
 nombre ::= chiffre nombre
          | chiffre
chiffre ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9</code></pre>
  <p>Le mot que nous voulons reconnaître est <code>7 + 35</code>. La règle
  racine est <code><em>exp</em></code>. Si nous la dérivons (de gauche à droite
  et de haut en bas, ou <em lang="en">left-to-right</em> et
  <em lang="en">top-to-bottom</em> en anglais), nous pouvons avoir
  <code><em>exp</em> + <em>exp</em></code> ou <code><em>nombre</em></code> (la
  <strong>disjonction</strong>, <em>i.e.</em> le « ou », est représentée par le
  symbole « <code>|</code> ») :</p>
  <pre><code>exp + exp | nombre
→ exp + exp
→ ( exp + exp | nombre ) + exp
→ nombre + exp
→ ( chiffre nombre | chiffre ) + exp</code></pre>
  <p>Nous continuons à dériver jusqu'à <strong>éliminer</strong> toutes les
  règles et n'avoir que des <strong>lexèmes</strong> :</p>
  <pre><code>…
→ ( chiffre nombre | chiffre ) + exp
→ chiffre + exp
→ ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ) + exp
→ 7 + exp
→ 7 + ( exp + exp | nombre )
→ 7 + nombre
→ 7 + ( chiffre nombre | chiffre )
→ 7 + chiffre nombre
→ 7 + ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ) nombre
→ 7 + 3 nombre
→ 7 + 3 ( chiffre nombre | chiffre )
→ 7 + 3 chiffre
→ 7 + 3 ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 )
→ 7 + 3 5</code></pre>
  <p>Une dériviation existe bel et bien pour reconnaître le mot <code>7 +
  35</code>, c'est donc un mot valide pour ces règles.</p>
  <p>Un ensemble de règles est appelé une <strong>grammaire</strong>. Et donc,
  une grammaire représente un <strong>langage</strong> !</p>
  <p>Toutefois, il existe plusieurs catégories de grammaires. C'est en 1956 qu'a
  été formulée la
  <a href="https://fr.wikipedia.org/wiki/Hi%C3%A9rarchie_de_Chomsky">hiérarchie
  de Chomsky</a>, classant les grammaires en quatre
  <strong>niveaux</strong> :</p>
  <ol>
    <li>grammaires <strong>générales</strong>, ou <em lang="en">unrestricted
    grammars</em>, reconnaissant les langages dits de Turing, aucune
    restriction n'est imposée aux règles ;</li>
    <li>grammaires <strong>contextuelles</strong>, ou
    <em lang="en">context-sensitive grammars</em>, reconnaissant les langages
    contextuels ;</li>
    <li>grammaires <strong>algébriques</strong>, ou <em lang="en">context-free
    grammars</em>, reconnaissant les langages algébriques, basés sur les
    automates à pile ;</li>
    <li>grammaires <strong>régulières</strong>, ou <em lang="en">regular
    grammars</em>, reconnaissant les langages réguliers.</li>
  </ol>
  <p>Chaque niveau reconnait le niveau suivant. <code>Hoa\Compiler</code> ne
  traite que les langages définis par les grammaires de niveau 3 et 4. Pour
  donner rapidement une idée, les grammaires régulières peuvent s'apparenter aux
  <a href="https://fr.wikipedia.org/wiki/Expression_régulière">expressions
  régulières</a> (comme les <a href="http://pcre.org/">PCRE</a>), bien connues
  des développeurs. Mais les grammaires régulières ne permettent pas par exemple
  de reconnaître des <strong>couples de symboles</strong> (comme des
  parenthèses, des accolades ou des guillemets), alors que les grammaires
  algébriques le permettent (grâce à la notion de piles de lexèmes).</p>

  <h3 id="Matching_words" for="main-toc">Reconnaissance de mots</h3>

  <div id="parsers" class="schema"></div>
  <script>
  Hoa.Document.onReady(function ( ) {

      var paper    = Hoa.Graph(Hoa.$('#parsers'), 800, 180);
      var grid     = paper.grid(0, 0, 800, 180, 5, 2);
      var word     = grid.push(paper.rect(0, 0, 140, 80, 3, 'mot'), 0, 0);
      var sequence = grid.push(paper.rect(0, 0, 140, 80, 3, 'séquence'), 2, 0);
      var trace    = grid.push(paper.rect(0, 0, 140, 80, 3, 'résultat'), 4, 0);
      grid.push(paper.rect(0, 0, 140, 50, 3, 'abcdef'), 0, 1);
      grid.push(paper.rect(0, 0, 380, 50, 3, '[[a ⟼ …], [bc ⟼ …], [d ⟼ …], [ef ⟼ …]]'), 2, 1);
      grid.push(paper.rect(0, 0, 140, 50, 3, 'valide/invalide'), 4, 1);

      paper.link.between(word, sequence, 'analyseur lexical');
      paper.link.between(sequence, trace, 'analyseur syntaxique');
  });
  </script>
  <p>En général, le processus de compilation débute par deux
  <strong>analyses</strong> : <strong>lexicale</strong> et
  <strong>syntaxique</strong>. Une analyse lexicale consiste à
  <strong>découper</strong> un mot en une <strong>séquence de lexèmes</strong>.
  Cette séquence sera ensuite utilisée par l'analyseur syntaxique afin de
  vérifier que le mot <strong>appartient</strong> au langage.</p>
  <p>Selon la grammaire, la reconnaissance ne se fera pas de la même manière,
  mais le principe reste identique : prendre les lexèmes les uns après les
  autres dans la séquence et vérifier qu'ils permettent
  d'<strong>avancer</strong> dans la <strong>dérivation</strong> des règles de
  notre grammaire.</p>
  <p>Les analyses syntaxiques sont aussi classées en
  <strong>catégories</strong> : LL, LR, LALR etc. <code>Hoa\Compiler</code> ne
  propose que des analyseurs syntaxiques LL, pour <em lang="en">Left-to-right
  Leftmost derivation</em>, <em>i.e.</em> de la plus haute règle vers la plus
  profonde, et les règles sont dérivées de la gauche vers la droite. Là encore,
  il existe des sous-catégories, dont deux que traite
  <code>Hoa\Compiler</code> : LL(1) et LL(*). D'une manière générale, nous
  parlons d'analyseurs syntaxiques LL(<em>k</em>) : si un lexème ne permet pas
  de dériver une règle comme il faut, alors l'analyseur peut
  <strong>revenir</strong> jusqu'à <em>k</em> étapes en arrière ; nous parlons
  aussi de <em lang="en">backtrack</em>.  Autrement dit, les règles peuvent être
  <strong>ambiguës</strong> : à chaque fois que nous dérivons une règle de la
  grammaire, nous avons plusieurs choix possibles et l'analyseur peut se
  tromper, c'est pourquoi il doit parfois revenir en arrière. La variable
  <em>k</em> permet de définir le <strong>niveau</strong> d'ambiguïté. Si une
  grammaire peut être analysée par un analyseur syntaxique LL(1), elle est dite
  <strong>non-ambiguë</strong> : à chaque lexème utilisé pour dériver nos
  règles, il n'y a qu'un seul choix possible. Et si nous avons un analyseur
  syntaxique LL(*), cela signifie que la variable <em>k</em> est
  <strong>indéfinie</strong>. L'exemple suivant illustre une grammaire
  non-ambiguë :</p>
  <pre><code>rule ::= a b c | d e f</code></pre>
  <p>Et cet exemple illustre une grammaire ambiguë :</p>
  <pre><code>rule1 ::= a rule2
rule2 ::= b rule3 | b rule4
rule3 ::= c d
rule4 ::= e f</code></pre>
  <p>Voyons quand nous essayons de trouver une dérivation pour le mot
  <code>abef</code> à partir de la règle racine <code>rule1</code> :</p>
  <pre><code>rule1
→ a rule2                   <em>   a  bef ✔</em>
  → a (b rule3 | b rule4)   <em>   a  bef</em>
    → a b rule3             <em>  ab  ef  ✔</em>
      → a b c d             <em> abe  f   ✖</em>
    ← a b rule3             <em>  ab  ef  ✖</em>
  ← a (b rule3 | b rule4)   <em>   a  bef</em>
    → a b rule4             <em>  ab  ef  ✔</em>
      → a b e f             <em>abef      ✔</em></code></pre>
  <p>La règle <code>rule2</code> est ambiguë, ce qui peut entraîner une mauvaise
  dérivation et donc un retour en arrière, un
  <em lang="en">backtracking</em>.</p>

  <h2 id="LLk_compiler-compiler" for="main-toc">Compilateur de compilateurs
  LL(<em>k</em>)</h2>

  <p>Écrire des compilateurs est une tâche <strong>laborieuse</strong>. Ce n'est
  pas forcément toujours difficile mais souvent répétitif et long. C'est
  pourquoi il existe des <strong>compilateurs de compilateurs</strong>, ou
  autrement dit, des générateurs de compilateurs. La plupart du temps, ces
  compilateurs de compilateurs utilisent un langage
  <strong>intermédiaire</strong> pour écrire une grammaire. La bibliothèque
  <code>Hoa\Compiler</code> propose la classe <code>Hoa\Compiler\Llk\Llk</code>
  qui permet l'écriture de compilateurs de compilateurs à travers un langage
  <strong>dédié</strong>.</p>

  <h3 id="PP_language" for="main-toc">Langage PP</h3>

  <p>Le langage PP, pour <em lang="en">PHP Parser</em>, permet d'exprimer des
  <strong>grammaires algébriques</strong>. Il s'écrit dans des fichiers portant
  l'extension <code>.pp</code> (voir le fichier
  <a href="@central_resource:path=Library/Compiler/.Mime"><code>hoa://Library/Compiler/.Mime</code></a>).</p>
  <p>Une grammaire est constituée de <strong>lexèmes</strong> et de
  <strong>règles</strong>. La déclaration d'un lexème se fait de la manière
  suivante : <code>%token <em>namespace_in</em>:<em>name</em> <em>value</em> ->
  <em>namespace_out</em></code>, où <code><em>name</em></code> représente le
  <strong>nom</strong> du lexème, <code><em>value</em></code> représente sa
  <strong>valeur</strong>, au format <a href="http://pcre.org/">PCRE</a>
  (attention à ne pas reconnaître de valeur vide, auquel cas une exception sera
  levée), et <code><em>namespace_in</em></code> et
  <code><em>namespace_out</em></code> représentent les noms des <strong>espaces
  de noms</strong> et sont optionels (vaut <code>default</code> par défaut). Par
  exemple <code>number</code> qui représente un nombre composé de chiffres de
  <code>0</code> à <code>9</code> :</p>
  <pre><code class="language-pp">%token number \d+</code></pre>
  <p>Les espaces de noms représentent des <strong>sous-ensembles</strong>
  disjoints de lexèmes, utilisés pour <strong>faciliter</strong> les analyses.
  Une déclaration <code>%skip</code> est similaire à <code>%token</code>
  excepté qu'elle représente un lexème à <strong>sauter</strong>, c'est à dire
  à ne pas considérer. Un exemple courant de lexèmes <code>%skip</code> est les
  espaces :</p>
  <pre><code class="language-pp">%skip space \s</code></pre>
  <p>Pour expliquer les règles, nous allons utiliser comme exemple la grammaire
  <code>Json.pp</code>, grammaire légèrement <strong>simplifiée</strong> du
  <a href="http://json.org/">langage JSON</a> (voir la
  <a href="https://tools.ietf.org/html/rfc4627">RFC4627</a>). La grammaire
  <strong>complète</strong> se situe dans le fichier
  <a href="@central_resource:path=Library/Json/Grammar.pp"><code>hoa://Library/Json/Grammar.pp</code></a>.
  Ainsi :</p>
  <pre><code class="language-pp">%skip   space          \s
// Scalars.
%token  true           true
%token  false          false
%token  null           null
// Strings.
%token  quote_         "        -> string
%token  string:string  [^"]+
%token  string:_quote  "        -> default
// Objects.
%token  brace_         {
%token _brace          }
// Arrays.
%token  bracket_       \[
%token _bracket        \]
// Rest.
%token  colon          :
%token  comma          ,
%token  number         \d+

value:
    &amp;lt;true> | &amp;lt;false> | &amp;lt;null> | string() | object() | array() | number()

string:
    ::quote_:: &amp;lt;string> ::_quote::

number:
    &amp;lt;number>

#object:
    ::brace_:: pair() ( ::comma:: pair() )* ::_brace::

#pair:
    string() ::colon:: value()

#array:
    ::bracket_:: value() ( ::comma:: value() )* ::_bracket::</code></pre>
  <p>Nous remarquons que nous avons deux espaces de noms pour les lexèmes :
  <code>default</code> et <code>string</code> (cela permet de ne pas
  <strong>confondre</strong> les lexèmes <code>quote_</code> et
  <code>string:_quote</code> qui ont la même représentation). Nous remarquons
  ensuite la règle <code>value</code> qui est une <strong>disjonction</strong>
  de plusieurs lexèmes et règles. Les <strong>constructions</strong> du langage
  PP sont les suivantes :</p>
  <ul>
    <li><code><em>rule</em>()</code> pour <strong>appeler</strong> une
    règle ;</li>
    <li><code>&amp;lt;<em>token</em>></code> et <code>::<em>token</em>::</code>
    pour <strong>déclarer</strong> un lexème (les espaces de noms n'apparaissent
    pas ici) ;</li>
    <li><code>|</code> pour une <strong>disjonction</strong> (un choix) ;</li>
    <li><code>(<em>…</em>)</code> pour grouper ;</li>
    <li><code><em>e</em>?</code> pour dire que <code><em>e</em></code> est
    <strong>optionel</strong> ;</li>
    <li><code><em>e</em>+</code> pour dire que <code><em>e</em></code> peut
    apparaître <strong>1 ou plusieurs</strong> fois ;</li>
    <li><code><em>e</em>*</code> pour dire que <code><em>e</em></code> peut
    apparaître <strong>0 ou plusieurs</strong> fois ;</li>
    <li><code><em>e</em>{<em>x</em>,<em>y</em>}</code> pour dire que
    <code><em>e</em></code> peut apparaître entre <code><em>x</em></code> et
    <code><em>y</em></code> fois ;</li>
    <li><code>#<em>node</em></code> pour créer un <strong>nœud</strong>
    <code><em>node</em></code> dans l'arbre ;</li>
    <li><code><em>token</em>[<em>i</em>]</code> pour <strong>unifier</strong>
    des lexèmes entre eux.</li>
  </ul>
  <p>Peu de constructions mais amplement suffisantes.</p>
  <p>Enfin, la grammaire du langage PP est écrite… dans le langage PP ! Vous
  pourrez la trouver dans le fichier
  <a href="@central_resource:path=Library/Compiler/Llk/Llk.pp"><code>hoa://Library/Compiler/Llk/Llk.pp</code></a>.</p>

  <h3 id="Compilation_process" for="main-toc">Processus de compilation</h3>

  <div id="overview" class="schema"></div>
  <script>
  Hoa.Document.onReady(function ( ) {

      var paper = Hoa.Graph(Hoa.$('#overview'), 800, 360);
      var flow  = paper.grid(0, 0, 800, 360, 1, 4);
      flow.push(paper.rect(0, 0, 200, 70, 3, 'analyseur lexical'));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'analyseur syntaxique'));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'trace'));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'AST'));

      var annot = paper.grid(180, 0, 80, 360, 1, 4);
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '1'));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '2'));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '3'));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '4'));
  });
  </script>
  <p>Le processus de compilation qu'utilise <code>Hoa\Compiler\Llk\Llk</code>
  est classique. Il commence par analyser <strong>lexicalement</strong> la
  donnée textuelle, le mot, <em>i.e.</em> à transformer notre donnée en une
  séquence de lexèmes. L'<strong>ordre</strong> de déclaration des lexèmes est
  primordial car l'analyseur lexical va les prendre les uns après les autres.
  Ensuite, c'est l'analyseur <strong>syntaxique</strong> qui entre en jeu afin
  de <strong>reconnaître</strong> notre donnée.</p>
  <p>Si l'analyse syntaxique est un succès, nous obtenons une
  <strong>trace</strong>. Cette trace peut être transformée en AST, pour
  <em lang="en">Abstract Syntax Tree</em>. Cet arbre représente notre donnée
  textuelle après analyse. Il a l'avantage de pouvoir être visité (nous
  détaillerons plus loin), ce qui permet par exemple d'ajouter de nouvelles
  <strong>contraintes</strong> qui ne peuvent pas être exprimées dans la
  grammaire, comme une vérification de type.</p>
  <p>Manipulons un peu <code>Hoa\Compiler\Llk\Llk</code>. Cette classe est un
  <strong>assistant</strong> pour lire une grammaire au format PP facilement.
  Elle prend en seul argument un flux en lecture vers la grammaire et retourne
  un compilateur <code>Hoa\Compiler\Llk\Parser</code> prêt à l'emploi. Sur ce
  compilateur, nous allons appeler la méthode
  <code>Hoa\Compiler\Llk\Parser::parse</code> pour analyser une donnée JSON. Si
  la donnée est correcte, nous aurons en retour un AST, sinon une exception sera
  levée. Enfin, nous allons utiliser le visiteur
  <code>Hoa\Compiler\Visitor\Dump</code> pour afficher notre AST :</p>
  <pre><code class="language-php">// 1. Load grammar.
$compiler = Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp'));

// 2. Parse a data.
$ast      = $compiler->parse('{"foo": true, "bar": [null, 42]}');

// 3. Dump the AST.
$dump     = new Hoa\Compiler\Visitor\Dump();
echo $dump->visit($ast);

/**
 * Will output:
 *     >  #object
 *     >  >  #pair
 *     >  >  >  token(string:string, foo)
 *     >  >  >  token(true, true)
 *     >  >  #pair
 *     >  >  >  token(string:string, bar)
 *     >  >  >  #array
 *     >  >  >  >  token(null, null)
 *     >  >  >  >  token(number, 42)
 */</code></pre>
  <p>Quand nous écrivons et testons une grammaire, nous allons répéter ces trois
  tâches très <strong>régulièrement</strong>. C'est pourquoi, le script
  <code>hoa</code> propose la commande <code>compiler:pp</code>. Cette commande
  propose d'analyser une donnée par rapport à une grammaire et d'appliquer un
  visiteur si besoin sur l'AST résultant. Notons que la lecture de la donnée
  peut se faire à travers un
  <a href="https://en.wikipedia.org/wiki/Pipeline_(Unix)"><em lang="en">pipe</em></a> :</p>
  <pre><code class="language-shell">$ echo '[1, [1, [2, 3], 5], 8]' | hoa compiler:pp Json.pp 0 --visitor dump
>  #array
>  >  token(number, 1)
>  >  #array
>  >  >  token(number, 1)
>  >  >  #array
>  >  >  >  token(number, 2)
>  >  >  >  token(number, 3)
>  >  >  token(number, 5)
>  >  token(number, 8)</code></pre>
  <p>C'est un moyen pratique pour tester <strong>rapidement</strong> des données
  par rapport à notre grammaire. Il ne faut pas hésiter à regarder l'aide de la
  commande <code>compiler:pp</code> pour plus de détails !</p>
  <p>Les analyses s'effectuent sur la règle <strong>racine</strong> mais nous
  pouvons préciser une <strong>autre règle</strong> à l'aide du deuxième
  argument de la méthode <code>Hoa\Compiler\Llk\Parser::parse</code> :</p>
  <pre><code class="language-php">$compiler->parse('{"foo": true, "bar": [null, 42]}', 'object');</code></pre>
  <p>Pour utiliser la règle racine, il suffit de passer <code>null</code>.</p>
  <p>Et enfin, pour ne pas générer l'AST mais uniquement savoir si la donnée est
  valide ou pas, nous pouvons utiliser le dernier argument de notre méthode en
  lui passant <code>false</code> :</p>
  <pre><code class="language-php">$valid = $compiler->parse('{"foo": true, "bar": [null, 42]}', null, false);
var_dump($valid);

/**
 * Will output:
 *     bool(true)
 */</code></pre>

  <h4 id="Errors" for="main-toc">Erreurs</h4>

  <p>Les erreurs de compilation sont remontées à travers des exceptions,
  ainsi :</p>
  <pre><code class="language-shell">$ echo '{"foo" true}' | hoa compiler:pp Json.pp 0 --visitor dump
Uncaught exception (Hoa\Compiler\Exception\UnexpectedToken):
Hoa\Compiler\Llk\Parser::parse(): (0) Unexpected token "true" (true) at line 1
and column 8:
{"foo" true}
       ↑
in hoa://Library/Compiler/Llk/Parser.php at line 1</code></pre>
  <p>Plusieurs exceptions peuvent remonter selon le contexte :</p>
  <ul>
    <li>durant l'analyse <strong>lexicale</strong>,
    <code>Hoa\Compiler\Exception\UnrecognizedToken</code> quand un lexème n'est
    pas reconnu, <em>i.e.</em> quand la donnée textuelle ne peut plus être
    découpée en une séquence de lexèmes, et
    <code>Hoa\Compiler\Exception\Lexer</code> quand d'autres erreurs plus
    générales arrivent, par exemple si un lexème reconnaît une valeur
    vide ;</li>
    <li>durant l'analyse <strong>syntaxique</strong>,
    <code>Hoa\Compiler\Exception\UnexpectedToken</code> quand un lexème n'est
    pas attendu, <em>i.e.</em> qu'il ne permet plus de dériver les règles de la
    grammaire.</li>
  </ul>
  <p>L'exception parente est <code>Hoa\Compiler\Exception\Exception</code>.</p>

  <h4 id="Nodes" for="main-toc">Nœuds</h4>

  <p>Le processus de compilation aboutit très souvent à la
  <strong>production</strong> d'un AST. Il est important de contrôler sa
  <strong>forme</strong>, sa <strong>taille</strong>, les données qu'il
  <strong>contient</strong> etc. C'est pourquoi il est nécessaire de comprendre
  la notation <code>#<em>node</em></code> car elle permet de créer des
  <strong>nœuds</strong> dans l'AST. Une précision tout d'abord, les lexèmes
  déclarés avec la syntaxe <code>&amp;lt;<em>token</em>></code> apparaîtront
  dans l'arbre, alors que les autres lexèmes, déclarés avec la syntaxe
  <code>::<em>token</em>::</code>, n'y apparaîtront pas. En effet, dans notre
  dernier exemple, les lexèmes <code>quote_</code>, <code>brace_</code>,
  <code>colon</code>, <code>comma</code> etc. n'apparaissent pas. Ensuite, il
  est important de noter que les déclarations de nœuds se
  <strong>surchargent</strong> les unes par rapport aux autres au sein d'une
  <strong>même règle</strong>. Enfin, un nom de règle peut être précédé par
  <code>#</code>, comme pour la règle <code>#array</code>, qui permet de définir
  un nœud par <strong>défaut</strong>, mais il peut être surchargé. Par exemple,
  si nous remplaçons la règle <code>array</code> par :</p>
  <pre><code class="language-pp">#array:
    ::bracket_:: value() ( ::comma:: value() #bigarray )* ::_bracket::</code></pre>
  <p>Si le tableau ne contient qu'une seule valeur, le nœud s'appelera
  <code>#array</code>, sinon il s'appelera <code>#bigarray</code> ; voyons
  plutôt :</p>
  <pre><code class="language-shell">$ echo '[42]' | hoa compiler:pp Json.pp 0 --visitor dump
>  #array
>  >  token(number, 42)
$ echo '[4, 2]' | hoa compiler:pp Json.pp 0 --visitor dump
>  #bigarray
>  >  token(number, 4)
>  >  token(number, 2)</code></pre>
  <p>Bien sûr, il peut arriver qu'un nœud soit créé ou pas selon le dérivation
  <strong>empruntée</strong> dans la règle. Le mécanisme est normalement assez
  <strong>intuitif</strong>.</p>

  <h4 id="Namespaces" for="main-toc">Espace de noms</h4>

  <p>Détaillons un peu le fonctionnement de l'analyseur lexical vis à vis des
  <strong>espaces de noms</strong>.</p>
  <p>Les <strong>lexèmes</strong> sont placés dans des espaces de noms,
  c'est à dire que seuls les lexèmes de l'espace de noms
  <strong>courant</strong> seront utilisés par l'analyseur lexical. L'espace de
  noms par défaut est <code>default</code>. Pour déclarer l'espace de noms d'un
  lexème, il faut utiliser l'opérateur <code>:</code>.  Quand un lexème est
  consommé, il peut <strong>changer</strong> l'espace courant pour la suite de
  l'analyse lexicale, grâce à l'opérateur <code>-></code>.  Ainsi, nous allons
  déclarer cinq lexèmes : <code>foo</code> et <code>bar</code> dans l'espace
  <code>default</code>, <code>baz</code> dans <code>ns1</code> et
  <code>qux</code> et <code>foo</code> dans <code>ns2</code>. Le fait de
  déclarer deux fois <code>foo</code> n'est pas gênant car ils sont dans des
  espaces de noms <strong>différent</strong> :</p>
  <pre><code class="language-pp">%token      foo   fo+     -> ns1
%token      bar   ba?r+   -> ns2
%token  ns1:baz   ba?z+   -> default
%token  ns2:qux   qux+
%token  ns2:foo   FOO     -> ns1</code></pre>
  <p>Écrire <code>default:bar</code> est strictement équivalent à
  <code>bar</code>. Le lexème <code>foo</code> emmène dans <code>ns1</code>,
  <code>bar</code> dans <code>ns2</code>, <code>ns1:baz</code> dans
  <code>default</code>, <code>ns2:qux</code> reste dans <code>ns2</code> et
  <code>ns2:foo</code> emmène dans <code>ns1</code>. Observons la séquence de
  lexèmes produite par l'analyseur lexical avec la donnée
  <code>fooooobzzbarrrquxFOObaz</code> :</p>
  <pre><code class="language-php">$pp = '%token      foo   fo+     -> ns1'     . "\n" .
      '%token      bar   ba?r+   -> ns2'     . "\n" .
      '%token  ns1:baz   ba?z+   -> default' . "\n" .
      '%token  ns2:qux   qux+'               . "\n" .
      '%token  ns2:foo   FOO     -> ns1';

// 1. Parse PP.
Hoa\Compiler\Llk\Llk::parsePP($pp, $tokens, $rawRules);

// 2. Run the lexical analyzer.
$lexer    = new Hoa\Compiler\Llk\Lexer();
$sequence = $lexer->lexMe('fooooobzzbarrrquxFOObaz', $tokens);

// 3. Pretty-print the result.
$format   = '%' . (strlen((string) count($sequence)) + 1) . 's  ' .
            '%-13s %-20s  %-20s  %6s' . "\n";
$header   = sprintf($format, '#', 'namespace', 'token name', 'token value', 'offset');

echo $header, str_repeat('-', strlen($header)), "\n";

foreach ($sequence as $i => $token) {
    printf(
        $format,
        $i,
        $token['namespace'],
        $token['token'],
        $token['value'],
        $token['offset']
    );
}

/**
 * Will output:
 *      #  namespace     token name            token value           offset
 *     ---------------------------------------------------------------------
 *      0  default       foo                   fooooo                     0
 *      1  ns1           baz                   bzz                        6
 *      2  default       bar                   barrr                      9
 *      3  ns2           qux                   qux                       14
 *      4  ns2           foo                   FOO                       17
 *      5  ns1           baz                   baz                       20
 *      6  default       EOF                   EOF                       23
 */</code></pre>
  <p>Nous lisons les lexèmes, leur espace et leur valeur dans le tableau. La
  donnée a pu être <strong>découpée</strong>, et nous sommes passés d'un espace
  à un autre. Si cette fois nous essayons avec la donnée <code>foqux</code>,
  nous aurons une erreur : <code>fo</code> correspond au lexème <code>foo</code>
  dans l'espace <code>default</code>, nous changeons alors d'espace pour
  <code>ns1</code>, et là, aucun lexème dans cet espace ne peut reconnaître au
  moins <code>q</code>.  Une erreur sera levée.</p>
  <p>Jusqu'à maintenant, nous avons vu comment passer d'un espace à l'autre avec
  l'opérateur <code>-></code>. Aucun <strong>historique</strong> sur les espaces
  traversés n'est conservé. Toutefois, dans certains cas rares, il peut arriver
  qu'un espace de lexèmes soit accessible via <strong>plusieurs</strong> autres
  et que nous aimerions qu'un lexème déclenche le retour vers l'espace de noms
  <strong>précédent</strong>. Autrement dit, nous aimerions un historique des
  espaces traversés et pouvoir y naviguer (en arrière uniquement). C'est le rôle
  du mot-clé <code>__shift__ * <em>n</em></code>, à utiliser après l'opérateur
  <code>-></code> et à la place du nom d'un espace. <code>__shift__</code> est
  équivalent à dire : revient à l'espace précédent. <code>__shift__</code> est
  équivalent à <code>__shift__ * 1</code>, et
  <code>__shift__ * <em>n</em></code> à : revient <code><em>n</em></code> fois à
  l'espace précédent.</p>
  <p>Lorsque le mot-clé <code>__shift__</code> apparaît dans la grammaire, les
  espaces sont gérés comme une <strong>pile</strong>, d'où le vocabulaire
  <em lang="en">shift</em>. Il faut faire attention à bien dépiler les espaces
  pour ne pas avoir une pile trop conséquente.</p>
  <p>Prenons en exemple la grammaire suivante :</p>
  <pre><code class="language-pp">%token       foo1  a    -> ns1
%token       foo2  x    -> ns2
%token       end   e

%token   ns1:bar   b
%token   ns1:baz   c    -> ns3
%token   ns1:tada  t    -> __shift__

%token   ns2:bar   y
%token   ns2:baz   z    -> ns3
%token   ns2:tada  T    -> __shift__

%token   ns3:qux   =   -> __shift__

#test:
    ( &amp;lt;foo1> | &amp;lt;foo2> ) &amp;lt;bar> &amp;lt;baz> &amp;lt;qux> &amp;lt;tada> &amp;lt;end></code></pre>
  <p>Cette grammaire reconnaît deux données : <code>abc=te</code> et
  <code>xyz=Te</code>. Si le premier lexème <code>foo1</code> est rencontré,
  l'analyseur syntaxique changera d'espace pour <code>ns1</code>. Quand il
  rencontrera le lexème <code>ns1:baz</code>, il passera dans <code>ns3</code>.
  Ensuite, quand il rencontrera <code>ns3:qux</code>, il reviendra à l'espace
  précédent, soit <code>ns1</code>. Et ainsi de suite. Maintenant, s'il ne
  rencontre pas <code>foo1</code> mais <code>foo2</code>, il ira dans l'espace
  <code>ns2</code>, puis dans <code>ns3</code> puis à nouveau <code>ns2</code>.
  Les mots-clés <code>__shift__</code> pour <code>ns1:tada</code> et
  <code>ns2:tada</code> n'ont pas d'autres buts que de dépiler les espaces, mais
  aucune ambiguïté n'existe : ces espaces ne sont accessibles que par un seul
  espace, à savoir <code>default</code>.</p>
  <p>Maintenant, enregistrons cette grammaire dans un fichier
  <code>NamespaceStack.pp</code> et utilisons l'option
  <code>-s/--token-sequence</code> de la commande <code>hoa
  compiler:pp</code> :</p>
  <pre><code class="language-shell">$ echo -n 'abc=te' | hoa compiler:pp NamespaceStack.pp 0 --token-sequence
 #  namespace     token name            token value                     offset
-------------------------------------------------------------------------------
 0  default       foo1                  a                                    0
 1  ns1           bar                   b                                    1
 2  ns1           baz                   c                                    2
 3  ns3           qux                   =                                    3
 4  ns1           tada                  t                                    4
 5  default       end                   e                                    5
 6  default       EOF                   EOF                                  6

$ echo -n 'xyz=Te' | hoa compiler:pp NamespaceStack.pp 0 --token-sequence
 #  namespace     token name            token value                     offset
-------------------------------------------------------------------------------
 0  default       foo2                  x                                    0
 1  ns2           bar                   y                                    1
 2  ns2           baz                   z                                    2
 3  ns3           qux                   =                                    3
 4  ns2           tada                  T                                    4
 5  default       end                   e                                    5
 6  default       EOF                   EOF                                  6</code></pre>
  <p>Nous voyons que l'analyse lexicale a réussi à jongler avec les espaces de
  noms, comme attendu. Nous avions deux façons d'accéder à l'espace
  <code>ns3</code> : soit depuis <code>ns1</code>, soit depuis <code>ns2</code>.
  L'analyseur a réussi à créer un historique des espaces et à y naviguer.</p>

  <h4 id="Unification" for="main-toc">Unification</h4>

  <p>Une caractéristique qu'apporte le langage PP par rapport à d'autres
  langages de grammaires connus est la capacité d'exprimer une
  <strong>unification</strong> de lexèmes. Imaginons la grammaire
  suivante :</p>
  <pre><code class="language-pp">%token  quote   '|"
%token  string  \w+

rule:
    ::quote:: &amp;lt;string> ::quote::</code></pre>
  <p>Les guillemets qui entourent la chaîne de caractère peuvent être de deux
  sortes : simple, avec le symbole « <code>'</code> », ou double, avec le
  symbole « <code>"</code> ». Ainsi, les données <code>"foo"</code> et
  <code>'foo'</code> sont valides, mais <strong>également</strong>
  <code>"foo'</code> et <code>'foo"</code> !</p>
  <p>L'unification des lexèmes permet d'ajouter une <strong>contrainte</strong>
  supplémentaire sur la <strong>valeur</strong> des lexèmes à l'exécution. La
  syntaxe est la suivante : <code><em>token</em>[<em>i</em>]</code>. La valeur
  de <code><em>i</em></code> indique quels lexèmes vont devoir porter la même
  valeur. Et enfin, l'unification est <strong>locale</strong> à une instance
  d'une règle, c'est à dire qu'il n'y a pas d'unification entre les lexèmes de
  plusieurs règles et que cela s'applique sur la règle <strong>appelée</strong>
  uniquement. Ainsi, l'exemple devient :</p>
  <pre><code class="language-pp">rule:
    ::quote[0]:: &amp;lt;string> ::quote[0]::</code></pre>
  <p>Ce qui invalide les données <code>"foo'</code> et <code>'foo"</code>.</p>
  <p>L'unification trouve de nombreuses applications, comme par exemple unifier
  les noms des balises ouvrantes et fermantes du
  <a href="http://w3.org/TR/xml11/">langage XML</a>.</p>

  <h3 id="Abstract_Syntax_Tree" for="main-toc"><em lang="en">Abstract Syntax
  Tree</em></h3>

  <div id="overview_ast" class="schema"></div>
  <script>
  Hoa.Document.onReady(function ( ) {

      var paper = Hoa.Graph(Hoa.$('#overview_ast'), 800, 360);
      var flow  = paper.grid(0, 0, 800, 360, 1, 4);
      flow.push(paper.rect(0, 0, 200, 70, 3, 'analyseur lexical').attr({opacity: .3}));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'analyseur syntaxique').attr({opacity: .3}));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'trace').attr({opacity: .3}));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'AST'));

      var annot = paper.grid(180, 0, 80, 360, 1, 4);
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '1').attr({opacity: .3}));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '2').attr({opacity: .3}));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '3').attr({opacity: .3}));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '4'));
  });
  </script>
  <p>Un arbre <strong>syntaxique abstrait</strong> représente une donnée
  textuelle sous forme <strong>structurelle</strong>. Chaque
  <strong>nœud</strong> de cet arbre est représenté par la classe
  <code>Hoa\Compiler\Llk\TreeNode</code>. Parmis les méthodes utiles, nous
  trouvons :</p>
  <ul>
    <li><code>getId</code> pour obtenir l'identifiant du nœud ;</li>
    <li><code>getValueToken</code> pour obtenir le nom du lexème ;</li>
    <li><code>getValueValue</code> pour obtenir la valeur du lexème ;</li>
    <li><code>isToken</code> si le nœud représente un lexème ;</li>
    <li><code>getChild(<em>$i</em>)</code> pour obtenir le
    <code><em>$i</em></code>-ème enfant d'un nœud ;</li>
    <li><code>getChildren</code> pour obtenir tous les nœuds ;</li>
    <li><code>getChildrenNumber</code> pour connaître le nombre d'enfants ;</li>
    <li><code>getParent</code> pour obtenir le nœud parent ;</li>
    <li><code>getData</code> pour obtenir une référence vers un tableau de
    données ;</li>
    <li><code>accept</code> pour appliquer un visiteur.</li>
  </ul>
  <p>Les visiteurs sont le moyen le plus pratique pour
  <strong>parcourir</strong> un AST. En guise d'exemple, nous allons écrire le
  visiteur <code>PrettyPrinter</code> qui va réécrire une donnée JSON avec notre
  propre convention (espacements, retours à la ligne etc.). Un visiteur doit
  implémenter l'interface <code>Hoa\Visitor\Visit</code> et n'aura qu'une seule
  méthode à écrire : <code>visit</code> qui prend trois arguments :
  l'élément et deux arguments accessoires (en référence et en copie). Voyons
  plutôt :</p>
  <pre><code class="language-php">class PrettyPrinter implements Hoa\Visitor\Visit
{
    public function visit (
        Hoa\Visitor\Element $element,
        &amp;amp;$handle = null,
        $eldnah = null
    ) {
        static $i      = 0;
        static $indent = '    ';

        // One behaviour per node in the AST.
        switch ($element->getId()) {
            // Object: { … }.
            case '#object':
                echo '{', "\n";
                ++$i;

                foreach ($element->getChildren() as $e => $child) {
                    if (0 &amp;lt; $e) {
                        echo ',', "\n";
                    }

                    echo str_repeat($indent, $i);
                    $child->accept($this, $handle, $eldnah);
                }

                echo "\n", str_repeat($indent, --$i), '}';

                break;

            // Array: [ … ].
            case '#array':
                echo '[', "\n";
                ++$i;

                foreach ($element->getChildren() as $e => $child) {
                    if (0 &amp;lt; $e) {
                        echo ',', "\n";
                    }

                    echo str_repeat($indent, $i);
                    $child->accept($this, $handle, $eldnah);
                }

                echo "\n", str_repeat($indent, --$i), ']';

                break;

            // Pair: "…": ….
            case '#pair':
                echo
                    $element->getChild(0)->accept($this, $handle, $eldnah),
                    ': ',
                    $element->getChild(1)->accept($this, $handle, $eldnah);

                break;

            // Many tokens.
            case 'token':
                switch ($element->getValueToken()) {
                    // String: "…".
                    case 'string':
                        echo '"', $element->getValueValue(), '"';

                        break;

                    // Booleans.
                    case 'true':
                    case 'false':

                    // Null.
                    case 'null':

                    // Number.
                    case 'number':
                        echo $element->getValueValue();

                        break;
                }

                break;
        }
    }
}</code></pre>
  <p>Nous allons voir tout de suite un exemple d'utilisation :</p>
  <pre><code class="language-php">$compiler    = Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp'));
$ast         = $compiler->parse('{"foo": true, "bar": [null, 42]}');
$prettyPrint = new PrettyPrinter();
echo $prettyPrint->visit($ast);

/**
 * Will output:
 *     {
 *         "foo": true,
 *         "bar": [
 *             null,
 *             42
 *         ]
 *     }
 */</code></pre>
  <p>La méthode <code>getData</code> est très pratique pour
  <strong>stocker</strong> des données susceptibles d'être
  <strong>réutilisées</strong>, par exemple d'un visiteur à l'autre. Cette
  méthode retourne une <strong>référence</strong> sur un tableau ; ainsi :</p>
  <pre><code class="language-php">$data = $element->getData();

if (!isset($data['previousComputing'])) {
    throw new Exception('Need a previous computing.', 0);
}

$previous = $data['previousComputing'];</code></pre>
  <p>Il est courant d'utiliser un visiteur par <strong>contrainte</strong> :
  vérifiation des symboles, vérification de types etc. Certains peuvent laisser
  des données nécessaires pour le suivant. La méthode <code>getData</code>
  n'impose aucune structuration des données, elle propose uniquement un accès à
  un tableau. Ce sera à vous de faire le reste.</p>
  <p>Utiliser la classe <code>Hoa\Compiler\Llk\TreeNode</code> est vraiment
  <strong>trivial</strong> et nous l'utiliserons la plupart du temps avec un
  visiteur.</p>

  <h3 id="Traces" for="main-toc">Traces</h3>

  <div id="overview_trace" class="schema"></div>
  <script>
  Hoa.Document.onReady(function ( ) {

      var paper = Hoa.Graph(Hoa.$('#overview_trace'), 800, 360);
      var flow  = paper.grid(0, 0, 800, 360, 1, 4);
      flow.push(paper.rect(0, 0, 200, 70, 3, 'analyseur lexical').attr({opacity: .3}));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'analyseur syntaxique').attr({opacity: .3}));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'trace'));
      flow.push(paper.rect(0, 0, 200, 70, 3, 'AST').attr({opacity: .3}));

      var annot = paper.grid(180, 0, 80, 360, 1, 4);
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '1').attr({opacity: .3}));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '2').attr({opacity: .3}));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '3'));
      annot.push(paper.rect(0, 0, 45, 45, 22.5, '4').attr({opacity: .3}));
  });
  </script>
  <p>Le compilateur LL(<em>k</em>) que propose Hoa est clairement découpé en
  plusieurs <strong>couches</strong>. Chaque couche est exploitable pour
  permettre une modularité maximum. Quand la grammaire est traduite en « règles
  machines » et que les analyseurs lexical et syntaxique ont validé une donnée,
  il en résulte une <strong>trace</strong>. Cette trace est un tableau composé
  de trois classes seulement :</p>
  <ul>
    <li><code>Hoa\Compiler\Llk\Rule\Entry</code> quand l'analyseur syntaxique
    est rentré dans une règle ;</li>
    <li><code>Hoa\Compiler\Llk\Rule\Ekzit</code> quand l'analyseur syntaxique
    est sorti d'une règle ;</li>
    <li><code>Hoa\Compiler\Llk\Rule\Token</code> quand l'analyseur syntaxique a
    rencontré un lexème.</li>
  </ul>
  <p>Nous pouvons l'obtenir grâce à la méthode
  <code>Hoa\Compiler\Llk\Parser::getTrace</code>. Pour bien comprendre cette
  trace, nous allons commencer par un exemple :</p>
  <pre><code class="language-php">$compiler = Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp'));
$ast      = $compiler->parse('{"foo": true, "bar": [null, 42]}');
$i        = 0;

foreach ($compiler->getTrace() as $element) {
    if ($element instanceof Hoa\Compiler\Llk\Rule\Entry) {
        echo str_repeat('>   ', ++$i), 'enter ', $element->getRule(), "\n";
    } elseif ($element instanceof Hoa\Compiler\Llk\Rule\Token) {
        echo
            str_repeat('    ', $i + 1), 'token ', $element->getTokenName(),
            ', consumed ', $element->getValue(), "\n";
    } else {
        echo str_repeat('&amp;lt;   ', $i--), 'ekzit ', $element->getRule(), "\n";
    }
}

/**
 * Will output:
 *     >   enter value
 *     >   >   enter object
 *                 token brace_, consumed {
 *     >   >   >   enter pair
 *     >   >   >   >   enter string
 *                         token quote_, consumed "
 *                         token string, consumed foo
 *                         token _quote, consumed "
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit string
 *                     token colon, consumed :
 *     >   >   >   >   enter value
 *                         token true, consumed true
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit value
 *     &amp;lt;   &amp;lt;   &amp;lt;   ekzit pair
 *     >   >   >   enter 13
 *     >   >   >   >   enter 12
 *                         token comma, consumed ,
 *     >   >   >   >   >   enter pair
 *     >   >   >   >   >   >   enter string
 *                                 token quote_, consumed "
 *                                 token string, consumed bar
 *                                 token _quote, consumed "
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit string
 *                             token colon, consumed :
 *     >   >   >   >   >   >   enter value
 *     >   >   >   >   >   >   >   enter array
 *                                     token bracket_, consumed [
 *     >   >   >   >   >   >   >   >   enter value
 *                                         token null, consumed null
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit value
 *     >   >   >   >   >   >   >   >   enter 21
 *     >   >   >   >   >   >   >   >   >   enter 20
 *                                             token comma, consumed ,
 *     >   >   >   >   >   >   >   >   >   >   enter value
 *                                                 token number, consumed 42
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit value
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit 20
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit 21
 *                                     token _bracket, consumed ]
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit array
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit value
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit pair
 *     &amp;lt;   &amp;lt;   &amp;lt;   &amp;lt;   ekzit 12
 *     &amp;lt;   &amp;lt;   &amp;lt;   ekzit 13
 *                 token _brace, consumed }
 *     &amp;lt;   &amp;lt;   ekzit object
 *     &amp;lt;   ekzit value
 */</code></pre>
  <p>Cet exemple nous révèle plusieurs choses. Tout d'abord, les informations
  que nous donne la trace peuvent être utiles : si nous sommes sur une règle,
  nous avons son <strong>nom</strong> (avec la méthode <code>getRule</code>), et
  si nous sommes sur un lexème, nous avons son <strong>nom</strong> (avec la
  méthode <code>getTokenName</code>), sa <strong>représentation</strong> (sous
  la forme d'une PCRE, avec la méthode <code>getRepresentation</code>), sa
  <strong>valeur</strong> (avec la méthode <code>getValue</code>), si c'est un
  nœud à <strong>conserver</strong> dans l'AST (avec la méthode
  <code>isKept</code>) et son index d'<strong>unification</strong> s'il existe
  (avec la méthode <code>getUnificationIndex</code>). Bien sûr, tout ceci est
  modifiable, ce qui peut influencer la construction de l'AST qui est le
  processus (optionnel) suivant.</p>
  <p>Ensuite, nous remarquons que parfois nous entrons dans une règle qui existe
  dans la grammaire, comme <code>object</code>, <code>pair</code>,
  <code>value</code> etc., et parfois nous entrons dans une règle
  <strong>numérique</strong>, comme <code>13</code>, <code>12</code>,
  <code>21</code>, <code>20</code> etc. Quand la grammaire est interprétée pour
  être transformée en « règles machines », chacune de ses règles est
  <strong>linéarisée</strong> selon les opérateurs du langage PP :</p>
  <ul>
    <li><code>Hoa\Compiler\Llk\Rule\Choice</code> pour une disjonction ;</li>
    <li><code>Hoa\Compiler\Llk\Rule\Concatenation</code> pour une
    concaténation ;</li>
    <li><code>Hoa\Compiler\Llk\Rule\Repetition</code> pour une répétition ;</li>
    <li><code>Hoa\Compiler\Llk\Rule\Token</code> pour un lexème (déjà vu
    précédemment).</li>
  </ul>
  <p>Toutes les règles sous ce format sont accessibles à travers la méthode
  <code>Hoa\Compiler\Llk\Parser::getRules</code> sous la forme d'un tableau.
  Nous allons afficher toutes les règles <strong>accessibles</strong> depuis la
  règle racine pour mieux comprendre :</p>
  <pre><code class="language-php">$compiler = Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp'));

// 1. Get all rules.
$rules    = $compiler->getRules();

// 2. Start with the root rule.
$stack    = [$compiler->getRootRule() => true];

while (false !== current($stack)) {
    $rule = key($stack);
    next($stack);
    echo
        "\n", '"', $rule, '" is a ',
        strtolower(substr(get_class($rules[$rule]), 22));

    $subrules = $rules[$rule]->getContent();

    // 3a. Token.
    if (null === $subrules) {
        continue;
    }

    echo ' of rules: ';

    // 3b. Other rules.
    foreach ((array) $rules[$rule]->getContent() as $subrule) {
        if (!array_key_exists($subrule, $stack)) {
            // 4. Unshift new rules to print.
            $stack[$subrule] = true;
        }

        echo $subrule, ' ';
    }
}

/**
 * Will output:
 *     "value" is a choice of rules: 1 2 3 string object array number
 *     "1" is a token
 *     "2" is a token
 *     "3" is a token
 *     "string" is a concatenation of rules: 5 6 7
 *     "object" is a concatenation of rules: 10 pair 13 14
 *     "array" is a concatenation of rules: 18 value 21 22
 *     "number" is a token
 *     "5" is a token
 *     "6" is a token
 *     "7" is a token
 *     "10" is a token
 *     "pair" is a concatenation of rules: string 16 value
 *     "13" is a repetition of rules: 12
 *     "14" is a token
 *     "18" is a token
 *     "21" is a repetition of rules: 20
 *     "22" is a token
 *     "16" is a token
 *     "12" is a concatenation of rules: 11 pair
 *     "20" is a concatenation of rules: 19 value
 *     "11" is a token
 *     "19" is a token
 */</code></pre>
  <p>Si nous lisons la règle <code>object</code>, nous savons que c'est la
  concaténation des règles <code>10</code>, <code>pair</code>, <code>13</code>
  et <code>14</code>. <code>10</code> est un lexème, <code>pair</code> est la
  concaténation des règles <code>string</code>, <code>16</code> et
  <code>value</code>, et ainsi de suite. La grammaire initiale est transformée
  pour être sous sa forme la plus <strong>réduite</strong> possible. Ceci permet
  de <strong>raisonner</strong> beaucoup plus <strong>facilement</strong> et
  <strong>rapidement</strong> sur les règles. En effet, les traitements sur la
  grammaire ne sont pas réservés aux AST. À l'étape précédente, avec la trace,
  nous pouvons déjà effectuer des traitements.</p>

  <h3 id="Generation" for="main-toc">Génération</h3>

  <p>Une grammaire peut être utile pour deux objectifs :
  <strong>valider</strong> une donnée (si nous la voyons comme un automate) ou
  <strong>générer</strong> des données. Jusqu'à présent, nous avons vu comment
  valider une donnée à travers plusieurs processus :
  <strong>préparation</strong> de notre compilateur, exécution des
  <strong>analyseurs</strong> lexical et syntaxique résultant sur une
  <strong>trace</strong>, transformation de la trace vers un
  <strong>AST</strong> pour enfin <strong>visiter</strong> cet arbre. Mais nous
  pouvons nous arrêter à la première étape, la préparation de notre compilateur,
  pour exploiter les règles afin de générer une donnée qui sera valide par
  rapport à notre grammaire.</p>
  <p><code>Hoa\Compiler\Llk\Sampler</code> propose trois algorithmes de
  <strong>générations</strong> de données :</p>
  <ul>
    <li>génération aléatoire et uniforme ;</li>
    <li>génération exhaustive bornée ;</li>
    <li>génération basée sur la couverture.</li>
  </ul>
  <p>Pourquoi proposer trois algorithmes ? Parce qu'il est illusoire de penser
  qu'un seul algorithme peut suffir aux <strong>nombreux</strong> contextes
  d'utilisations. Chacun répond à des besoins différents, nous l'expliquerons
  plus loin.</p>
  <p>Générer une donnée à partir d'une grammaire se fait en <strong>deux
  étapes</strong> :</p>
  <ol>
    <li>générer des valeurs pour les <strong>lexèmes</strong> afin d'avoir des données
    brutes ;</li>
    <li>générer un <strong>chemin</strong> dans les règles de la grammaire.</li>
  </ol>
  <p>Un chemin est équivalent à une dérivation, le vocabulaire est différent
  selon notre objectif : validation ou génération.</p>

  <h4 id="Isotropic_generation_of_lexemes" for="main-toc">Génération
  isotropique de lexèmes</h4>

  <p>Pour générer les valeurs des lexèmes, peu importe l'algorithme utilisé, il
  doit être <strong>rapide</strong>. Nous allons utiliser un parcours dit
  <strong>isotrope</strong>. Nous partons d'une règle et nous avançons
  uniquement à partir de choix <strong>aléatoires</strong> et <strong>uniformes
  localement</strong> (uniquement pour ce choix). Par exemple si nous avons une
  disjonction entre trois sous-règles, nous allons tirer aléatoirement et
  uniformément entre 1 et 3. Si nous avons une concaténation, nous allons juste
  concaténer chaque partie. Et enfin, une répétition n'est rien d'autre qu'une
  disjonction de concaténation : en effet, <code><em>e</em>{1,3}</code> est
  strictement équivalent à <code><em>e</em> | <em>ee</em> | <em>eee</em></code>.
  Illustrons plutôt :</p>
  <pre><code>([ae]+|[x-z]!){1,3}              <em>repeat <em>[ae]+|[x-z]!</em> 2 times</em>
→ ([ae]+|[x-z]!)([ae]+|[x-z]!)  <em>choose between <em>[ae]+</em> and <em>[x-z]!</em></em>
→ ([ae]+)([ae]+|[x-z]!)         <em>repeat <code>ae</code> 2 times</em>
→ [ae][ae]([ae]+|[x-z]!)        <em>choose between <em>a</em> and <em>e</em></em>
→ e[ae]([ae]+|[x-z]!)           <em>again</em>
→ ea([ae]+|[x-z]!)              <em>choose between <em>[ae]+</em> and <em>[x-z]!</em></em>
→ ea([x-z]!)                    <em>choose between <em>x</em>, <em>y</em> and <em>z</em></em>
→ eay!</code></pre>
  <p>Cette génération est <strong>naïve</strong> mais ce n'est pas important. Ce
  qui est vraiment important est la génération des chemins dans les règles, ou
  autrement dit, la génération des <strong>séquences de lexèmes</strong>.</p>

  <h4 id="Uniform_random_generation" for="main-toc">Génération aléatoire
  et uniforme</h4>

  <p>Le premier algorithme est celui de la génération <strong>aléatoire</strong>
  et <strong>uniforme</strong>. Cet algorithme est utile si nous voulons générer
  des séquences de lexèmes de <strong>taille <em>n</em> fixe</strong> et avec
  une <strong>uniformité</strong> non pas locale (comme avec un parcours
  isotrope) mais sur l'<strong>ensemble</strong> de toutes les séquences
  possibles. Succinctement, l'algorithme travaille en deux étapes :
  <strong>pré-calcul</strong> (une seule fois par taille) puis
  <strong>génération</strong>. Le pré-calcul est une étape
  <strong>automatique</strong> et calcule le <strong>nombre</strong> de
  séquences et sous-séquences possibles de taille <em>n</em>. Cette étape aide
  au calcul de <strong>fonctions de répartions</strong> pour
  <strong>guider</strong> la génération des séquences de lexèmes finales.</p>
  <p>Nous allons générer 10 données aléatoires de taille 7, c'est à dire
  composées de 7 lexèmes. Pour cela, nous allons utiliser la classe
  <code>Hoa\Compiler\Llk\Sampler\Uniform</code> qui prend en premier argument
  notre grammaire, en deuxième le générateur de valeurs de lexèmes (ici
  <code>Hoa\Regex\Visitor\Isototropic</code>) et enfin la taille :</p>
  <pre><code class="language-php">$sampler = new Hoa\Compiler\Llk\Sampler\Uniform(
    // Grammar.
    Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp')),
    // Token sampler.
    new Hoa\Regex\Visitor\Isotropic(new Hoa\Math\Sampler\Random()),
    // Length.
    7
);

for ($i = 0; $i &amp;lt; 10; ++$i) {
    echo $i, ' => ', $sampler->uniform(), "\n";
}

/**
 * Will output:
 *     0 => [ false , null , null ]
 *     1 => [ " l " , null ]
 *     2 => [ [ true ] , true ]
 *     3 => [ [ [ 4435 ] ] ]
 *     4 => [ [ [ 9366 ] ] ]
 *     5 => [ true , false , null ]
 *     6 => { " |h&amp;lt;# " : false }
 *     7 => [ [ [ false ] ] ]
 *     8 => [ false , true , 7 ]
 *     9 => [ false , 5 , 79 ]
 */</code></pre>
  <p>Nous pouvons redéfinir la taille avec la méthode
  <code>Hoa\Compiler\Llk\Sampler\Uniform::setLength</code>. Nous aurons
  peut-être remarqué que certains opérateurs de répétition n'ont pas de bornes
  supérieures, comme <code>+</code> ou <code>*</code> ; dans ce cas, nous la
  définissons à <em>n</em>.</p>

  <h4 id="Bounded_exhaustive_generation" for="main-toc">Génération exhaustive
  bornée</h4>

  <p>Le deuxième algorithme est celui de la génération <strong>exhaustive
  bornée</strong>. Cet algorithme génère <strong>toutes</strong> les séquences
  (c'est le côté exhaustif) de lexèmes de taille 1 <strong>jusqu'à</strong>
  <em>n</em> (c'est le caractère borné). Un aspect très intéressant de cet
  algorithme est que l'exhaustivité est une forme de <strong>preuve</strong> !
  L'algorithme fonctionne comme un itérateur et est très simple à utiliser :</p>
  <pre><code class="language-php">$sampler = new Hoa\Compiler\Llk\Sampler\BoundedExhaustive(
    // Grammar.
    Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp')),
    // Token sampler.
    new Hoa\Regex\Visitor\Isotropic(new Hoa\Math\Sampler\Random()),
    // Length.
    7
);

foreach ($sampler as $i => $data) {
    echo $i, ' => ', $data, "\n";
}

/**
 * Will output:
 *     0 => true
 *     1 => false
 *     2 => null
 *     3 => " 8u2 "
 *     4 => { " ,M@aj{ " : true }
 *     5 => { " x`|V " : false }
 *     6 => { " NttB " : null }
 *     7 => { " eJWwA " : 0 }
 *     8 => [ true ]
 *     9 => [ true , true ]
 *     10 => [ true , true , true ]
 *     11 => [ true , true , false ]
 *     12 => [ true , true , null ]
 *     13 => [ true , true , 32 ]
 *     14 => [ true , false ]
 *     15 => [ true , false , true ]
 *     16 => [ true , false , false ]
 *     17 => [ true , false , null ]
 *     18 => [ true , false , 729 ]
 *     19 => [ true , null ]
 *     20 => [ true , null , true ]
 *     21 => [ true , null , false ]
 *     …
 *     157 => [ 780 , 01559 , 32 ]
 *     158 => 344
 */</code></pre>
  <p><em>A l'instar</em> de l'algorithme précédent, nous pouvons redéfinir la
  borne maximum avec la méthode
  <code>Hoa\Compiler\Llk\Sampler\BoundedExhaustive::setLength</code>. Et les
  opérateurs de répétition sans borne supérieure utilisent <em>n</em>.</p>

  <h4 id="Grammar_coverage-based_generation" for="main-toc">Génération basée
  sur la couverture</h4>

  <p>Le dernier algorithme est celui de la génération <strong>basée sur la
  couverture</strong>. Cet algorithme réduit l'<strong>explosion
  combinatoire</strong> rencontrée avec l'algorithme précédent mais l'objectif
  est tout autre : il va générer des séquences de lexèmes qui vont
  « <strong>activer</strong> » toutes les <strong>branches</strong> des règles
  de la grammaire. Une règle est dite couverte si et seulement si ses
  sous-règles sont toutes couvertes, et un lexème est dit couvert s'il a été
  utilisé dans une séquence. Pour assurer une <strong>diversité</strong> dans
  les séquences produites, les points de choix entre des sous-règles non
  couvertes sont résolus par tirages <strong>aléatoires</strong>. L'algorithme
  fonctionne également comme un itérateur :</p>
  <pre><code class="language-php">$sampler = new Hoa\Compiler\Llk\Sampler\Coverage(
    // Grammar.
    Hoa\Compiler\Llk\Llk::load(new Hoa\File\Read('Json.pp')),
    // Token sampler.
    new Hoa\Regex\Visitor\Isotropic(new Hoa\Math\Sampler\Random())
);

foreach ($sampler as $i => $data) {
    echo $i, ' => ', $data, "\n";
}

/**
 * Will output:
 *     0 => true
 *     1 => { " )o?bz " : null , " %3W) " : [ false , 130 , " 6 " ] }
 *     2 => [ { " ny  " : true } ]
 *     3 => { " Ne;[3 " : [ true , true ] , " th: " : true , " C[8} " : true }
 */</code></pre>
  <p>Pour éviter l'explosion combinatoire et assurer la
  <strong>termination</strong> de l'algorithme, nous utilisons
  l'<strong>heuristique</strong> suivante sur les opérateurs de
  <strong>répétition</strong> : <code>*</code> répétera <code>0</code>,
  <code>1</code> et <code>2</code> fois, <code>+</code> répétera <code>1</code>
  et <code>2</code> fois, et enfin <code>{<em>x</em>,<em>y</em>}</code>
  répétera <code><em>x</em></code>, <code><em>x</em> + 1</code>,
  <code><em>y</em> - 1</code> et <code><em>y</em></code> fois. Cette heuristique
  trouve ses origines dans le test aux <strong>limites</strong>.</p>
  <p>Nous remarquons dans notre exemple que 4 données sont générées et suffisent
  à <strong>couvrir</strong> l'ensemble de notre grammaire !</p>

  <h4 id="Comparison_between_algorithms" for="main-toc">Comparaison entre
  les algorithmes</h4>

  <p>Voici quelques <strong>indices</strong> pour savoir quand utiliser tel ou
  tel autre algorithme.</p>
  <dl>
    <dt>Génération aléatoire et uniforme :</dt>
    <dd><ul>
      <li data-item="+">rapide pour des petites données, grande diversité dans
      les données et taille fixe ;</li>
      <li data-item="-">la phase de pré-calcul est exponentielle et donc
      longue bien que, ensuite, la génération soit très rapide.</li>
    </ul></dd>
    <dt>Génération exhaustive bornée :</dt>
    <dd><ul>
      <li data-item="+">rapide pour des petites données et l'exhaustivité est
      efficace ;</li>
      <li data-item="-">nombre exponentiel de données.</li>
    </ul></dd>
    <dt>Génération basée sur la couverture :</dt>
    <dd><ul>
      <li data-item="+">rapide pour des données de taille moyenne ou grande,
      et diversité des données ;</li>
      <li data-item="-">ne considère pas de taille.</li>
    </ul></dd>
  </dl>

  <h2 id="LL1_compiler-compiler" for="main-toc">Compilateur de compilateurs
  LL(1)</h2>

  <p>La documentation pour le compilateur LL(1), à travers la classe
  <code>Hoa\Compiler\Ll1</code>, n'est pas encore écrite. L'objectif est
  différent de <code>Hoa\Compiler\Llk</code> : il n'existe pas de langage
  intermédiaire, les automates sont codés en dur avec des tableaux et ce type de
  compilateurs est orienté haute performance. C'est pourquoi son comportement
  est <strong>monolithique</strong>.</p>

  <h2 id="Conclusion" for="main-toc">Conclusion</h2>

  <p><code>Hoa\Compiler</code> propose deux <strong>compilateurs de
  compilateurs</strong> : <code>Hoa\Compiler\Llk</code> et
  <code>Hoa\Compiler\Ll1</code>, afin de <strong>valider</strong> des données.
  Le <strong>langage PP</strong> permet d'écrire des <strong>grammaires
  algébriques</strong> de manière <strong>simple</strong> et
  <strong>naturelle</strong>. Le compilateur de compilateurs LL(<em>k</em>) est
  découpé en <strong>processus distincts</strong> ce qui le rend très
  <em lang="en">hackable</em>. Enfin, différents algorithmes permettent de
  <strong>générer</strong> des données à partir d'une grammaire selon le
  contexte d'utilisation.</p>

</yield>
</overlay>
