XL (langage)
XL, dont les lettres proviennent de eXtensible Language, est un langage de programmation, basé sur la programmation par concepts[1], développé depuis 2000 par Christophe de Dinechin.
XL (eXtensible Language) | |
Date de première version | 2000 |
---|---|
Paradigmes | Programmation impérative |
DĂ©veloppeur | Christophe de Dinechin |
Dernière version | 0.1 (2010) |
Typage | Fort |
Influencé par | Ada, C++ |
Système d'exploitation | Type Unix |
Licence | GPL |
Site web | XLR |
XL offre la possibilité de modifier et programmer la syntaxe et la sémantique du langage. Des plugins compilés peuvent être utilisés pour ajouter de nouvelles fonctionnalités au langage. On peut par exemple noter un ensemble de plugins permettant la mis en œuvre d'un langage impératif. Les utilisateurs peuvent écrire eux-mêmes leurs propres plugins pour permettre l'utilisation de syntaxe spécifiques, par exemple pour la dérivée, qui pourront être utilisés de la même manière que les fonctionnalités originales.
Langage
XL est défini en quatre niveaux différents :
- XL0 définit comment le texte entré est converti en un arbre syntaxique
- XL1 définit un langage de base avec des possibilités comparables au c++
- XL2 définit la bibliothèque standard qui inclut les types de données communs ainsi que les opérateurs
- XLR définit un Moteur d'exécution dynamique pour XL basé sur XL0
XL n'a ni type primitif ni mot-clé. Tous les opérateurs et les types de données, tels que les entiers ou les additions, sont définis dans la bibliothèque standard XL2. XL1 est portable dans différents environnements d'exécution. Ce qui n'est pas garanti pour XL2 qui n'offre pas de tels garanties : si un processeur particulier n'implémente pas la multiplication en virgule flottante, la définition de l'opérateur correspondant peut être manquante de la bibliothèque standard, et l'utilisation de cette fonctionnalité peut générer une erreur de compilation.
En XL, le programme Hello World peut s'Ă©crire ainsi :
use XL.TEXT_IO WriteLn "Hello World"
Une manière plus convenable pour des programmes plus importants serait :
import IO = XL.TEXT_IO IO.WriteLn "Hello World"
Une implémentation par récurrence de la fonction factorielle :
0! → 1 N! → N * (N-1)!
Syntaxe
La syntaxe est définie au niveau XL0. La phase XL0 du compilateur peut être configurée en utilisant un fichier de description de la syntaxe, où sont définies des propriétés telles que la représentation du texte et les priorités des opérateurs. Un fichier de syntaxe basique définit les notations mathématiques communes tel que + pour l'addition, ainsi que la priorité des opérations.
L'arbre syntaxique est composé de 7 types de nœuds : 4 types de feuilles (entier, réel, texte et symbole) et 3 types de nœuds internes (infixe, préfixe et bloc).
- un nœud entier est la représentation littérale d'un entier, comme
2
. Le signe#
peut indiquer une base différente de 10, par exemple (2#1001
). Un caractère souligné (ou underscore) comme séparateur peut aider la lecture, par exemple1_000_000
. - un nœud réel représente un nombre non entier, comme
2.5
. Les notations de base et les séparateurs peuvent être utilisés de la même façon pour les entiers, par exemple16#F.FFF#E-10
est une représentation littérale valide d'un réel. - un nœud texte représente un contenu textuel. Il est classiquement entouré par un guillemet simple ou double, comme
"Salut"
ou'a'
, mais le fichier syntaxique peut être utilisé pour ajouter d'autres séparateurs, dont ceux dédiés aux textes à lignes multiples. - un nœud symbole représente un nom ou un opérateur. Un nom est une séquence de caractères alpha-numériques commençant par une lettre, par exemple
Salut
. XL0 préserve la casse, mais XL1 ignore la casse et le caractère souligné ce qui fait queMonsieurDupont
etMonsieur_Dupont
sont le même nom. Un symbole est une séquence de caractères non alpha-numériques, par exemple*
ou=/=
. - un nœud infixe représente deux nœuds liés par une notation infixée, par exemple
A+1
ou2 and 3
. En particulier, un nœud infixe combiné à un infixe "nouvelle-ligne" permet de séparer deux lignes. - un nœud préfixe représente deux nœuds consécutifs, par exemple
Write "Salut"
. Ceci est aussi utilisé dans la notation postfixe, par exemple3!
ouOpen?
. - un nœud bloc représente un nœud entouré par des symboles isolants, par exemple
(A)
ou[Index]
. Une indentation est représentée en interne par un nœud bloc.
Avec le fichier de syntaxe par défaut, le code suivant est valide pour XL0, indépendamment de la sémantique :
A = B + "Salut"
Il est décomposé en :
infix("=", symbol("A"), infix("+", symbol("B"), text("Salut")))
SĂ©mantique de XL1
Le niveau XL1 est défini comme une séquence d'opérations sur l'arbre syntaxique du niveau XL0. Ces opérations sont effectuées par divers plugins du compilateur qui sont appelés suivant la forme de l'arbre syntaxique.
Des constructions particulières, translate
et translation
, sont fournies par un plugin destiné à faciliter l'écriture d'autres plugins. La construction quote
génère un arbre syntaxique. Voici comment ces constructions peuvent être utilisées pour implémenter un plugin nommé ZeroRemoval
, qui supprime les additions et multiplications par zéro :
translation ZeroRemoval when 'X' + 0 then return X when 'X' * 0 then return parse_tree(0)
Un plugin peut être invoqué sur un fichier complet à partir de la ligne de commande, ou plus localement dans le source du code en utilisant la notation pragma, comme ici :
X := {Differentiate} d(sin(omega * T) * exp(-T/T0)) / dT
Le niveau XL1 contient un grand ensemble de plugins, notamment XLSemantics
qui donne les abstractions communes telles que fonction, type de données et déclaration de variable et définition, et aussi les ordres de base de la programmation structurée que sont les boucles et les conditions.
Système de typage
XL1 contrôle le type statique, avec des possibilités de programmation générique qui vont au-delà de celles de C++ ou Ada. Les types comme les tableaux ou les pointeurs, qui sont des types primitifs en C++, sont déclarés dans une bibliothèque dans XL. Par exemple, le type d'un tableau d'une dimension peut être défini par :
generic [Item : type; Size : integer] type array
Un type générique validé est un type générique où une condition indique comment le type peut être utilisé. De tels types ne doivent pas avoir de paramètres génériques. Par exemple, on peut déclarer un type comme ordered
(ordonné) si un opérateur inférieur est présent :
// Un type est ordonné en présence d'un opérateur inférieur
generic type ordered if
A, B : ordered
Test : boolean := A < B
Il est alors possible de déclarer une fonction qui est implicitement générique puisque le type ordered
est lui-même ordonné.
// Function générique pour au moins un élément function Min(X : ordered) return ordered is return X
Ceci s'applique aussi aux types génériques qui ont des paramètres, comme array
. Une fonction calculant la somme des éléments dans un tableau peut s'écrire :
function Sum(A : array) return array.Item is for I in 0..array.Size-1 loop result += A[I]
Type protégé de liste de variable d'arguments
Les fonctions peuvent être surchargées. Une fonction peut être déclarée avec un nombre variable d'arguments grâce au mot other
dans la liste des paramètres. Dans une telle fonction, other
permet de passer un nombre variable d'arguments Ă une autre fonction :
// Fonction générique pour le minimum de N éléments
function Min(X : ordered;…) return ordered is
result := Min(…)
if X < result then
result := X
Quand ce genre de fonction est appelée, le compilateur invoque les fonctions de manière récursive pour correspondre à la liste :
// Exemple d'utilisation de la fonction Min précédente X : real := Min(1.3, 2.56, 7.21) Y : integer := Min(1, 3, 6, 7, 1, 2)
Réduction d'expression (surcharge d'opérateur)
Les opérateurs peuvent être définis en utilisant la forme written
de déclaration de fonction. Voici un code qui déclare l'addition d'entiers :
function Add(X, Y: integer) return integer written X+Y
De telles formes écrites peuvent avoir plus de deux paramètres. Par exemple, une transformation de matrice linéaire peut s'écrire :
function Linear(A, B, C : matrix) return matrix written A+B*C
Une forme écrite peut utiliser des constantes, et ce type de forme est plus spécialisée que sans constantes. Par exemple :
function Equal(A, B : matrix) return boolean written A=B function IsNull(A : matrix) return boolean written A=0 function IsUnity(A : matrix) return boolean written A=1
Ce mécanisme est utilisé pour décrire tous les opérateurs de base. Une expression est progressivement réduite à des appels utilisant des formes écrites. Pour cette raison, ce mécanisme est appelé réduction d'expression plutôt que surcharge d'opérateur.
Itérateurs
Les itérateurs de XL permettent de programmer à la fois des générateurs et des itérateurs.
import IO = XL.UI.CONSOLE
iterator IntegerIterator (var out Counter : integer; Low, High : integer) written Counter in Low..High is
Counter := Low
while Counter <= High loop
yield
Counter += 1
// I doit être déclaré, car indiqué comme 'var out' dans l'itérateur
// Une déclaration implicite de I comme entier est alors faite ici
for I in 1..5 loop
IO.WriteLn "I=", I
SĂ©mantique de XLR
XLR est un langage dynamique, conçu dès l'origine comme un exécutant (back-end) pour le compilateur XL1 (d'où le nom, qui représente le runtime de XL). Il partage la syntaxe de base XL0 avec XL1, mais son comportement est plus proche de celui d'un langage fonctionnel, même si XL1 est censé ressembler plus à un langage impératif. XLR n'a pratiquement qu'un seul opérateur de construction, "→", qui indique une réécriture. La notation à gauche de la notation de la réécriture est transformée en notation à droite de la réécriture.
Ce mécanisme est utilisé pour implémenter les notations standards :
si vrai alors PartieVraie sinon PartieFausse → PartieVraie si faux alors PartieVraie sinon PartieFausse → PartieFausse
État du développement et historique
Le design de XL a commencé aux alentours de 1992, sous le nom LX (Langage eXpérimental). Le projet a été mis dans le domaine du logiciel libre en 2000, dans le cadre du projet « Mozart » qui visait à fournir des capacités de métaprogrammation multi-langage. Les premiers compilateurs étaient écrits en C++, mais cela rendait l'écriture d'extensions de compilateurs compliquée, car C++ ne permettait pas d'offrir le niveau d'abstraction voulu (par exemple un équivalent de translate
). Les aspects multi-langage rendaient les arbres syntaxiques difficile à manipuler en respectant la sémantique de chaque langage.
Une réécriture complète du compilateur a commencé en 2003, abandonnant l'idée de support multi-langage qui avait montré ses limites, et focalisant sur la facilité d'écriture des extensions. L'arbre syntaxique abstrait a été réduit à sept types de nœud seulement pour représenter tous les programmes. Ce nouveau compilateur s'est auto-compilé en 2004 (bien qu'avec des capacités réduites et une dépendance à C++ dans le code généré). Depuis, tout le développement se fait en XL.
En 2007, le langage offre un grand nombre de fonctionnalités avancées, mais manque encore d'une bibliothèque fonctionnelle.
Prédécesseurs
XL a été inspiré par un grand nombre d'autres langages. Dans l'ordre alphabétique :
- Ada a inspiré quelques fonctionnements appliqués sur la totalité d'un programme, comme la gestion d'exception, les tâches, la portabilité.
- Basic, principalement dans les versions modernes qui s'affranchissent des numéros de lignes et permettent la programmation structurée, a montré combien un langage de programmation peut être simple. Basic est un des premiers langages modernes ne nécessitant pas la présence de parenthèse autour des appels de routine.
- C a servi comme standard pour l'exécution (runtime) et le niveau machine. XL ne tourne pas sur une machine virtuelle.
- C++ par ses bibliothèques de modèles standards a montré le besoin d'une bonne implémentation des types génériques, incluant implicitement les instanciations des génériques (ce qui fait défaut à Ada).
- Fortran poursuit et dépasse les performances de C et C++ sur les applications de calcul intensif, aidant l'identification des constructions de langage qui préviendrait des optimisations utiles.
- Java démontre l'importance d'un système de bibliothèques portables. Les conteneurs Java montrent aussi les limitations d'une approche non basée sur une programmation générique. L'interfaçage avec le code Java reste un défi intéressant pour XL.
- L'extensibilité de Lisp a été vue comme un facteur clé de sa survie et de son intérêt jusqu'à aujourd'hui. Lisp a été le premier langage à introduire les fonctionnalités de l'orienté objet, même s'il a été conçu des années avant que la programmation orientée objet ne soit inventée.
- Prolog a démontré que les paradigmes de programmation alternatifs sont parfois utiles et hautement productifs. Tout a été fait pour s'assurer qu'un plugin dans le style de Prolog puisse être écrit pour XL.
- Visual Basic a montré comment la représentation de l'arbre d'analyse peut être séparée de sa présentation visuelle. Peu de gens éditent des formulaires VB sous forme textuelle. On peut espérer que les plugins d'édition XL fourniront un jour des fonctionnalités similaires, en manipulant directement l'arbre d'analyse.
Notes et références
- (en) Phil Manchester, « Dip into Concept Programming », The Register,‎ (lire en ligne, consulté le )