Comment Prolog et les grammaires DCG permettaient de comprendre
le français bien avant que ChatGPT n'existe.
(Et comment ça nous sert encore pour le Web Sémantique.)
Aujourd'hui, on balance une phrase à ChatGPT et il « comprend ». Mais comment faisait-on avant ? Dans les années 80 et 90, le traitement du langage naturel (NLP) n'était pas une histoire de réseaux de neurones gargantuesques, mais de grammaires formelles, de logique, et de Prolog.
Cette annexe raconte cette époque — et montre que ces techniques restent profondément utiles pour le Web Sémantique. Parce qu'au fond, le problème est le même : transformer du texte en structures formelles (triplets, graphes RDF, faits logiques). Et Prolog, avec ses Definite Clause Grammars (DCG), fait ça avec une élégance que les LLM ne pourront jamais égaler (parce que les LLM n'ont pas d'élégance, ils ont des poids).
Le Natural Language Processing (TALN en français — Traitement Automatique du Langage Naturel) est le domaine qui cherche à faire comprendre du texte par un ordinateur. Avant 2015, ça ressemblait à ça :
split)Les étapes 4 et 5 sont celles qui nous intéressent ici. C'est là que les DCG de Prolog brillent : elles permettent d'écrire une grammaire directement dans le langage, et d'obtenir l'analyse sémantique gratuitement par unification.
Une DCG (Definite Clause Grammar) est une notation de grammaire intégrée à Prolog. Elle permet de définir des règles grammaticales avec la même syntaxe que les prédicats Prolog, et d'obtenir automatiquement un parseur (qui analyse) et un générateur (qui produit des phrases).
Une grammaire hors-contexte décrit la structure d'une langue avec
des règles de la forme NonTerminal → Suite de symboles.
Exemple minimal :
cfgphrase → groupe_nominal, groupe_verbal.
groupe_nominal → déterminant, nom.
groupe_verbal → verbe, groupe_nominal.
déterminant → [le] | [la] | [un] | [une].
nom → [chat] | [souris] | [chien].
verbe → [mange] | [poursuit] | [voit].
Cette grammaire reconnaît « le chat mange une souris », « un chien poursuit le chat », etc. Mais elle ne fait que vérifier la syntaxe. Elle ne construit pas de sens.
En Prolog, une DCG s'écrit avec --> (au lieu de :-) :
prolog% Règles de grammaire DCG phrase --> groupe_nominal, groupe_verbal. groupe_nominal --> determinant, nom. groupe_verbal --> verbe, groupe_nominal. determinant --> [le] ; [la] ; [un] ; [une]. nom --> [chat] ; [souris] ; [chien]. verbe --> [mange] ; [poursuit] ; [voit].
Et on interroge :
prolog% Analyse : est-ce une phrase valide ? ?- phrase([le, chat, mange, une, souris], []). true . % Génération : produire une phrase aléatoire ?- phrase(P, []). P = [le, chat, mange, le, chat] ; P = [le, chat, mange, le, souris] ; P = [le, chat, poursuit, le, chat] % ...
Le secret ? Prolog transforme chaque règle DCG en un prédicat avec deux arguments cachés : la liste de mots restante avant et après l'application de la règle. C'est un diff-list — une technique élégante pour parser sans consommer la mémoire.
Une DCG peut construire une structure de données pendant l'analyse. Il suffit d'ajouter un argument à chaque règle pour représenter le résultat :
prolog% DCG avec construction de l'arbre syntaxique phrase(phrase(GN, GV)) --> groupe_nominal(GN), groupe_verbal(GV). groupe_nominal(gn(D, N)) --> determinant(D), nom(N). groupe_verbal(gv(V, GN)) --> verbe(V), groupe_nominal(GN). determinant(le) --> [le]. determinant(un) --> [un]. nom(chat) --> [chat]. nom(souris) --> [souris]. verbe(mange) --> [mange]. verbe(poursuit) --> [poursuit].
Résultat :
prolog?- phrase(Arbre, [le, chat, poursuit, une, souris], []). Arbre = phrase(gn(le, chat), gv(poursuit, gn(une, souris))) % On peut visualiser l'arbre : % phrase % / \ % gn gv % / \ / \ % le chat poursuit gn % / \ % une souris
À ce stade, on a un parseur syntaxique complet en 15 lignes de Prolog. Pas de bibliothèque externe, pas de JSON, pas d'API. Juste une grammaire et le moteur d'unification.
Le vrai pouvoir des DCG, c'est qu'on peut directement produire une représentation sémantique pendant l'analyse. Au lieu de construire un arbre, on construit des prédicats logiques :
prolog% DCG qui produit directement des faits logiques phrase(sujet_verbe_objet(S, V, O)) --> sujet(S), verbe(V), objet(O). sujet(socrate) --> [socrate]. sujet(platon) --> [platon]. verbe(est) --> [est]. objet(mortel) --> [mortel]. objet(un_homme) --> [un, homme]. % Utilisation : ?- phrase(Triplet, [socrate, est, mortel], []). Triplet = sujet_verbe_objet(socrate, est, mortel).
Ce sujet_verbe_objet(socrate, est, mortel) ressemble
furieusement à un triplet RDF
(socrate rdf:type mortel). Coïncidence ? Pas du tout.
Le Web Sémantique est basé sur des triplets (sujet, prédicat, objet). On peut écrire une DCG qui, à partir d'une phrase française, produit directement un triplet ou même un graphe RDF complet.
prolog% ------------------ LEXIQUE ------------------ det --> [le] ; [la] ; [un] ; [une] ; [les] ; [des]. nom(chat) --> [chat] ; [chats]. nom(souris) --> [souris]. nom(socrate) --> [socrate]. nom(homme) --> [homme]. verbe_intrans(existe) --> [existe]. verbe_trans(mange) --> [mange]. verbe_copule(est) --> [est] ; [sont]. propriete(mortel) --> [mortel]. % adjectif attribut % ------------------ RÈGLES ------------------ % Sujet Verbe : "Socrate existe" % → triplet(Sujet, rdf:type, Classe) phrase(triplet(S, type, C)) --> sujet(S), verbe_intrans(existe). % Sujet Verbe_Être Propriété : "Socrate est mortel" % → triplet(Sujet, rdf:type, Propriete) phrase(triplet(S, type, P)) --> sujet(S), verbe_copule(est), propriete(P). % Sujet Verbe Objet : "le chat mange une souris" % → triplet(Sujet, verbe, Objet) phrase(triplet(S, V, O)) --> sujet(S), verbe_trans(V), objet(O). sujet(N) --> nom(N). sujet(N) --> det, nom(N). objet(N) --> nom(N). objet(N) --> det, nom(N).
Testons :
prolog?- phrase(T, [socrate, est, mortel], []). T = triplet(socrate, type, mortel) ?- phrase(T, [socrate, existe], []). T = triplet(socrate, type, existe) ?- phrase(T, [le, chat, mange, une, souris], []). T = triplet(chat, mange, souris) % On peut même générer des phrases à partir de triplets ! ?- phrase(triplet(socrate, type, mortel), Phrase, []). Phrase = [socrate, est, mortel]
Bim. Une DCG de 20 lignes qui transforme du français en triplets, et réciproquement. C'est ça, la puissance de Prolog : un programme = deux directions.
On peut pousser plus loin : une phrase comme « le chat poursuit une souris » peut générer plusieurs triplets, dont un nœud anonyme pour l'événement :
ttl# Résultat RDF : [] a :Poursuite ; :agent :chat ; :patient :souris . :chat a :Chat . :souris a :Souris .
En Prolog, on ajoute simplement un argument supplémentaire à la DCG pour collecter la liste des triplets générés par la phrase. Chaque règle grammaticale ajoute ses triplets à la liste, et à la fin on a un graphe RDF complet.
C'est exactement ce que fait le Cours #2 quand il construit des faits Prolog à partir de triplets, ou le Cours #3 quand il charge une ontologie OWL et infère de nouvelles connaissances. La différence est que là, on part du langage naturel au lieu de fichiers Turtle.
triplet(chat, mange, souris) peut produire
« le chat mange la souris », « un chat mange une souris », etc.
Les LLM font la même chose aujourd'hui, mais avec 175 milliards de
paramètres. Les DCG le faisaient avec 20 lignes. (Bon, les résultats
n'étaient pas aussi fluides, mais l'élégance…)
Les DCG et le NLP à base de règles sont largement remplacés par les LLM aujourd'hui. Mais les idées n'ont pas disparu — elles sont devenues les fondations silencieuses du NLP moderne.
| Approche DCG / règles | Équivalent moderne (LLM) |
|---|---|
| Grammaire explicite, transparente, débogable | Boîte noire : on ne sait pas pourquoi la réponse est celle-ci |
| Pas besoin de données d'entraînement | Nécessite des milliards de tokens et des GPU à 30 000 € |
| Production déterministe et reproductible | Stochastique : deux exécutions peuvent donner des résultats différents |
| Génération et analyse dans le même programme | Génération oui, analyse « formelle » non (pas de garantie) |
| Passe à l'échelle horizontalement (un Prolog ça tient sur un Raspberry Pi) | Passe à l'échelle verticalement (besoin de datacenters) |
Les DCG ne sont pas mortes. Elles survivent dans :
Le cours #2 construit un mini-Prolog capable de chaînage avant et de parsing de fichiers Turtle. Les DCG montrent une autre application de Prolog directement utile au cours :
Le format Turtle (sujet predicat objet .) est
exactement le genre de langage qu'une DCG peut parser.
Sujet-Verbe-Objet en RDF, c'est la même structure que
Sujet-Verbe-Complément en grammaire.
Une DCG pourrait transformer un corpus de phrases françaises en une base de triplets RDF, prête à être importée dans Fuseki (Cours #3) ou interrogée en SPARQL (Cours #5).
Les triplets extraits par la DCG peuvent alimenter le moteur de raisonnement OWL du Cours #3 : les faits deviennent des axiomes, et les inférences enrichissent le graphe.
Inversement, un triplet RDF peut être transformé en phrase française par la même DCG. C'est un début de NLG (Natural Language Generation) pour expliquer les résultats SPARQL en français.