Site d’Emmanuel Saint-James > Conférences > Philologie de la programmation


Philologie de la programmation

La question du langage en informatique a ceci de particulier qu’il s’agit toujours d’écrits. L’examiner relève donc moins de la linguistique, où l’oralité est incontournable, que de la philologie où elle est absente : les langages de programmation, malgré leur modernité, partagent avec les langues mortes d’être des langues muettes. En revanche leur épigraphie a peu d’importance, l’archéologie de leurs supports, perforés d’abord (ruban, carte et listing), performants ensuite, révélant peu d’impact sur leurs systèmes d’écriture.

Plus important est de distinguer des familles de langages et de s’interroger sur leurs rapports au travers de 8 problématiques survenues, parfois simultanément, dans l’histoire de la programmation. Une conclusion dégagera une ligne directrice dans l’évolution de la programmation, qui se termine aujourd’hui en ligne brisée.

Cette conférence à été donnée dans le cadre du Séminaire Codes Sources le 22/1/2026

Le geste et le texte

Abstraire pour assembler

Langages d’assemblage : la différence est dans le contexte

Programme = données

Traduire, c’est expliciter la mémoire

Un langage d’assemblage n’offre qu’une écriture plus commode du langage binaire d’un processeur précis ; les langages de programmation dits évolués ont pour but de dispenser le programmeur de gérer la mémoire, à des degrés divers selon les langages. Ils nécessitent d’être traduits ou interprétés, ces deux termes n’ayant pas en informatique les mêmes rapports qu’en linguistique traditionnelle ;

Comparaison entre traduction et interprétation

Un programme en langage C
avant sa traduction en M4 ou autre.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
long int prim(unsigned long int a)
{
 if (!(a%2)) return 2;
 if (a >= 3) {
   long int m = round(sqrt(a));
   long int i = 3;
   while (i < m)
     {
        if (!(a%i)) return i;
        i+=2;
     }
 }
 return 0;
}

Un programme en langage Lisp
près à l’interprétation.

(dl prim (a)
   (if (eq (% a 2) 0)
        2
        (if (< a 3)
                 0
             ((lambda (i m)
                   (if (eq (% a i) 0)
                     i
                     (if (< i m)
                             0
                        (self (+ i 2)))))
             3
             (round (sqrt a))))))
         

L’analyse syntaxique

L’analyse syntaxique est une opération dont la complexité dépend du langage :

Des grammaires pour qui ?

Quelques exemples de grammaires (les symboles terminaux sont en italiques, l’axiome est le premier symbole terminal employé) :

Désambiguez et Décontextualisez, sinon je craque

Il existe toute une série de questions sur les grammaires formelles, certaines irrésolues :

De la théorie grammaticale à sa pratique

Cette mathématisation de la linguistique, finalement peu convaincante pour les langues naturelles, a eu un grand impact sur l’informatique. Outre la maîtrise de l’analyse syntaxique des langages de programmation évolués, on lui doit certaines nouveautés :

La récursivité

Le mécanisme de pile est aussi apparu lorsqu’il a fallu calculer une fonction récursive, c’est-à-dire une fonction dont la définition fait appel à elle-même. Voici deux exemples canoniques, définis sur les entiers naturels :

La fonction de la fonction en programmation

Ces langages dits fonctionnels sont allés plus loin dans l’abstraction, de la mémoire en particulier mais pas seulement :

Pas d’étreinte fatale en cas d’heureux événement

Les différentes programmations dont il vient d’être question ne se confrontent jamais à la question du temps, ni relatif, ni absolu, pourtant incontournable pour programmer les systèmes d’exploitation (curieuse traduction de l’anglais operating system qui sous-entend plus facilement qu’il s’agit d’une machine et non d’un être humain). Ceux-ci sont confrontés à plusieurs problèmes :

Synchronisation et interruption

#!/bin/bash
trap 'echo "Je sors de cet enfer"; exit 2' INT TERM

cmd=$1
shift
for i in *
do
   $cmd $i &
   PID[$!]=$i
done
status=0
for i in *
do
   wait -n -p process
   e=$?
   echo processus sur ${PID[$process]} sort avec code de retour $e
   if [ $e -ne 0 ]
   then
       status=$e
   fi
done
exit $status

De l’échec considéré comme une réussite (à tort)

Les langages évolués ont dispensé le programmeur de gérer la mémoire, la programmation dite fonctionnelle était à la limite de l’affranchir de la conception de l’algorithme, ce que la dénommée intelligence artificielle à chercher à atteindre entièrement. Il convient cependant de clarifier les choses :

Fin de l’histoire des langages, début des académies

Quelques langages de programmation sont apparus plus récemment, souvent pour gérer commodément de nouveaux périphériques, mais aucun nouveau paradigme de programmation n’en est résulté. Les questions de bon usage, autrement dit de méthodologie, sont passées au premier plan. A côté du génie logiciel et des gestionnaires de versions distribués, outils indispensables aujourd’hui mais sans impact sur les aspects linguistiques des textes qu’ils gèrent, deux notions méritent l’attention en philologie de la programmation :

Héritage de classe

Depuis que les programmes se comptent en milliers de lignes, produits par plusieurs personnes de surcroît, le problème de la collision de noms de variables ou de fonctions, autrement dit leur homonymie, est devenue fréquente. Pour y faire face, a été introduit le mécanisme d’héritage de classe :

Du type à la preuve

Le contrôle de type consiste à vérifier, idéalement une fois pour toutes avant l’exécution, que toute opération s’applique à des arguments conformes. Ce contrôle est plus ou moins difficile selon les langages :

Classer et Typer, c’est tout ce que tu sais faire

Un exemple de classe héritant d’une autre et déclarant le type de ses variables et fonctions :
un validateur XML fondé sur la programmation événementielle de l’analyseur SAX disponible en PHP :

class sax_validateur_min {
   protected array $dtd;
   protected array $erreurs = array();
   function __construct(array $dtd)
   {
       $this->dtd =$dtd;
   }

   function ouvrante (XmlParser $phraseur, string $nom, array $attr) : void
   {
       if (!isset($this->dtd[$nom])) {
           $this->errreurs[]="Element $nom inconnu";
       } else {
           $att = $this->attributs($phraseur, $nom, $attr);
       }
   }

   function fermante (XmlParser $phraseur, string $nom) : void
   {}

   function texte (XmlParser $phraseur, string $texte) : void
   {}

   function attributs(XmlParser $phraseur, string $nom, array $attr) : array
   {
       $ok = array();
       $atts = $this->dtd[$nom][1];
       foreach ($atts as $att => list($type, $mode)) {
           if (isset($attr[$att])) {
               $ok[] = $att;
               unset($attr[$att]);
           } elseif ($mode == 'REQUIRED') {
               $this->erreur[]="Attribut $att de $nom absent";
           }
       }
       if ($attr) {
           $ids = join(" ", array_keys($attr));
           $this->erreur[]= . "$ids attributs de $nom inconnus";
       }
       return $ok;
   }
}

class sax_validateur_contenu extends sax_validateur_min {
   protected array $pile = array();
   protected array $ids = array();
   function ouvrante (XmlParser $phraseur, string $nom, array $attr) : void
   {
       if (!isset($this->dtd[$nom])) {
           $this->erreur[]= "Element $nom inconnu";
           array_push($this->pile, 'ANY');
       } else {
           $ok = $this->pile
               ? preg_match("@[^\w:-]" . $nom . "[^\w:-]@", end($this->pile))
               : ($nom == key($this->dtd));
           if (!$ok) $this->erreur[]= "Element $nom incongru";
           array_push($this->pile, ' ' . $this->dtd[$nom][0] . ' ');
           $att = $this->attributs($phraseur, $nom, $attr);
       }
   }
   function fermante (XmlParser $phraseur, string $nom) : void
   {
       array_pop($this->pile);
   }
   function texte (XmlParser $phraseur, string $texte) : void
   {
       if (trim($texte) AND !preg_match('@#PCDATA@', end($this->pile)))
           $this->erreur[] = "texte incongru";
   }
   function attributs(XmlParser $phraseur, string $nom, array $attr) : array
   {
       $attr = parent::attributs($phraseur, $nom, $attr);
       foreach ($attr as $nom => $val) {
           if ($this->dtd[$nom][1][0] == 'ID') {
               if (isset($this->ids[$val])) {
                   $this->erreur[] = "ID $val en double";
               } else {
                   $this->ids[$val] = 1;
               }
           }
       }
       return $attr;
   }
}

Conclusion : A ma gauche l’expressivité, à ma droite la sécurité

L’histoire de la programmation n’est sans doute pas terminée : certains problèmes restent ouverts et les innovations matérielles n’ont certainement pas fini de l’obliger à évoluer. En revanche, l’histoire de l’expressivité de la programmation est, sinon terminée, en tout cas sur une pente subitement descendante. La raison en est la priorité accordée à la sécurité, ce que l’on peut énoncer d’une manière troublante au regard de sa résonance politique :