Pour l'instant, la librairie de lecture est assez brute et ne propose aucune construction de haut niveau. Par exemple, on voudrait probablement avoir des lecteurs prêts pour les types élémentaires, et des combinateurs pour lire des listes ou des paires comme dans les autres librairies. Commençons donc par là.
Un entier est une séquence non-vide de chiffres, ne commençant pas par zéro, ou bien contenant uniquement 0 . On va donc commencer par écrire un combinateur lisant une séquence de valeurs. C'est donc une fonction prenant un lecteur, et lisant avec ce lecteur autant que possible. Nous allons l'écrire récursivement : on lit une valeur, puis on lit une séquence, ou bien on ne lit rien. D'ailleurs c'est ainsi que les grammaires sont écrites :
La lecture d'une séquence produit une liste. Avec notre librairie :
Il y a juste un petit problème technique avec cette définition : c'est une récursion qui ne termine jamais, puisque l'appel récursif se fait sur le même argument. En pratique, si on essaie de s'en servir on obtient un exception pour dépassement de la pile. Moralement, sequence a deux arguments, le lecteur des valeurs de la séquence, et l'entrée puisqu'un lecteur est une fonction. Ici la récursion fait décroitre l'entrée, donc il faudrait explicitement prendre l'entrée en argument pour obtenir une récursion qui termine.
Un autre solution est d'utiliser un combinateur spécialisé dans les récursions. Le voici (c'est presque toujours la même définition dans toutes les librairies), il prend simplement à sa charge la récursion :
On l'utilise ainsi pour construire le combinateur de séquence :
fix @@ fun reader_seq -> se comprend comme l'attribution du nom reader_seq à la valeur que l'on veut définir récursivement. Cela ne résoudra pas le problème de non-terminaison si read_value accepte le mot vide, mais ce serait une mauvaise grammaire dans ce cas. On utilise ce nom pour l'appel récursif. On en profite pour définir une séquence non-vide :
Il nous faut ensuite tester si un caractère est un chiffre ou pas. Idéalement on utilise une librairie pour cela, en général les librairies pour unicode possède des prédicats pour les classes de caractères. En OCaml char ne représente pas les caractères unicodes (il faudrait le remplacer par uchar et modifier TextInput). On code donc les prédicats directement.
On peut maintenant faire la lecture d'un entier en distinguant le cas du zéro :
Notons que grâce à l'ajout de la fonction sequence, nous avons tout ce qu'il faut pour faire un lecteur d'expressions régulières (et par exemple les entiers s'expriment comme des expressions régulières). Ce que les grammaires ont de plus que les expressions régulières, c'est la possibilité de faire des constructions récursives.
On a souvent à lire une chaîne de caractères précise dans le texte, par exemple un mot-clé du langage... Écrivons donc une fonction qui à une chaîne de caractères construit le lecteur de cette chaîne.
Première étape, pour des raisons pratiques nous allons transformer la chaîne de caractères en liste de caractères, et inversement.
Ensuite, on transforme chaque caractère par un lecteur pour ce caractère, et il nous reste alors à les séquencer. L'action associé à un séquencement va être d'ajouter le caractère lu à la liste des caractères. Notons que dans ce cas nous souhaitons un parenthésage à droite en priorité, pour pouvoir ajouter le premier caractère à la liste des autres caractères.
On introduit les lecteurs suivants pour lire des espaces (ou autres caractères invisibles) : un, au moins un, ou n'importe quel nombre d'espaces.
Souvent les grammaires permettent de mettre des quantités quelconques d'espacement entre deux lexèmes. On ne souhaite pas avoir systématiquement à écrire explicitement où les espacements sont autorisés. La solution la plus simple est de définir une version de l'opérateur de séquencement qui lit automatiquement les espaces entre les deux valeurs lus.
Les combinateurs de pairs ou de triplets se déduisent facilement à partir de séquencement.
Nous avons tout ce qu'il faut pour définir un lecteur d'arbres binaires :
Peut-on faire plus simple ?