Gufo: un langage de shell moderne!

Gufo est un prototype de langage de script moderne qui peut embarquer des commandes unix et servir de shell.

Pourquoi

Bash est, pour les utilisateurs techniques, l'interface texte majeure sous les systèmes GNU-linux.

La raison principale est la facilité à appeler des programmes externes et la possibilité de combiner des flux d'entrées/sorties dans l'environnement orienté fichier de GNU-linux.

En gardant conscience de la qualité et de la quantité de travail qui a été mise dans Bash, un système d'exploitation moderne mérite un langage de shell utilisant les techniques de compilation et d'interprétation modernes. En dehors de la simple exécution d'une ligne de commande, il est nécessaire de disposer d'un langage de shell permettant d'exécuter des scripts pouvant manipuler facilement des lignes de commandes (par exemple "nb_files=ls -l | wc -l"). Bash le permet mais d'une manière tout à fait archaïque.

Modestement, mais avec une certaine envie de voir ce genre d'évolution, j'ai commencé à imaginer un nouveau langage pour le shell. J'ai travaillé à cette idée à temps partiel depuis septembre 2017 (l'idée, je l'avais depuis plus longtemps). Le projet est loin d'être déjà utilisable mais peut être considéré comme une "preuve de concept" (proof of concept) permettant d'annoncer les idées principales qui seront utilisées et d'obtenir des retours.

Je considère les fonctionnalités suivante comme étant importante dans un langage 'moderne':

Dans l'état actuel de Gufo, beaucoup reste à faire, entre autre, le type-checker n'est pas terminé, il reste des questions sur l'utilisation des entrées/sorties (comment paramétrer leur affichage entre sortie standard et/ou stockage dans une variable), travail à faire pour rendre l'usage console agréable... L'objectif à terme est de fournir une version libre, mais je ne ferais cela que si le projet est suffisamment avancé au moins pour une utilisation basique ce qui n'est pas le cas aujourd hui. Cela dépend aussi des opportunités de financement du projet.

Une description utilisateur

Le meilleur moyen de gouter à Gufo, est de plonger dans quelques exemples compréhensibles aussi bien pour des débutants que pour des utilistateurs avancés.

Une simple commande

code

 ls

explication Une simple commande shell est un programme Gufo valide, c'est ce qui lui permettra d'être un language de shell. La commande ls permet de lister les fichiers du répertoire courant. Ce programme permettra donc l'affichage de cette liste.

Une commande avec redirection

code

ls -l | wc -l > nb_files.txt

explication Gufo gère les opérateurs de redirections du bash. Pour information, "|" redirige la sortie de la première commande ("ls -l") vers la deuxième commande ("wc -l"). ">" redirige la sortie du résultat vers le fichiers nb_files.txt. La encore, le but est d'imiter bash, tout en reconnaissant qu'on ne le fait que pour les opérateurs simples, sans aller dans les fonctionnalités avancées (et souvent occultes).

Gufo permet aussi à l'utilisateur d'interagir avec l'entrée standard dans le cas de programme interactif.

Stockage d'une commande dans une variable

code

let $a = ls -l | wc -l

explication Une variable en Gufo est toujours préfixé par un '$' pour qu'il n'y ai pas ambigüité avec une commande. 'let' est le mot clé permettant de déclarer une variable ou une fonction. Le résultat de la commande (les système unix renvoit 0 pour une exécution réussi ou un entier non-nul indiquant l'erreur) est atteignable via "$a.Cmd.res", le contenu du résultat via "$a.Cmd.print_std".

Une fonction basique

code

let $factoriel $c = 
  if ($c != 1) 
  then $c * ($factoriel ($c - 1))
  else 1

explication

Voici la fonction factoriel (qui ne dépend pas d'éventuelles lignes de commandes). Cette fonction prend un argument "$c", si celui-ci est différent de 0, la fonction s'appelle récursivement (avec $c décrémenté à chaque appel). La syntaxe, et de nombreux aspect du langage sont empruntés à Ocaml. Gufo est un langage fonctionnel (généralement pas d'effet de bord, une fonction prend des arguments et renvoi une valeur résultante).

Une fonction avancé

code

let $run $fichier =
  let $current_log = $fichier+".log" in(
  let $past_log = $fichier+".log.old" in(
  #  put current log as past past log
  let $as_log = cat $current_log > $past_log in (
    if $as_log.Cmd.res == (some 0) 
      then (./gufo_run.native $fichier > $current_log; diff -q $current_log $past_log)
      else (echo "create first log"; ./gufo_run.native $fichier > $current_log)
  )))

explication

Voici d'abord une explication générale: le but de cette fonction est d'exécuter le programme externe "./gufo_run.native" prenant en paramètre une chaine de caractères (contenu dans '$fichier') et de stocker son résultat dans le fichier référé par '$current_log'. dans le cas ou la commande à déjà été exécuté on veut afficher le diff par rapport au log de l'exécution précédente.

Voila le détails par ligne:

  1. déclaration de la fonction '$run' qui prend 1 argument '$fichier'. Les lignes suivantes représente le corps de la fonction.
  2. déclaration de la variable '$current_log' qui correspond à la concaténation de '$fichier' avec la chaine ".old". Le "in" indique la porté, délimiter par la paire de parenthèses, dans laquelle est utilisable la variable.
  3. similaire à la ligne précédente, pour définir un "log.old".
  4. un commentaire (commence toujours par # et se termine en fin de ligne).
  5. exécution de la commande 'cat $current_log > $past_log', le résultat est mis dans la variable '$as_log'.
  6. un if et sa condition: la condition est vrai si le résultat de la commande vaut 0 (le "some" sera détaillé mais permet de différencier un cas ou l'exécution de la commande s'est effectivement produit, d'un cas ou celle-ci ne ce serait pas produit (on aurait un "none).
  7. l'exécution dans le cas ou la condition est vérifié: exécute le programme "gufo_run.native avec comme argument '$fichier' en mettant le résultat dans le fichier indiqué par '$current_log' On exécute successivement la commande diff.
  8. l'exécution dans le cas ou la condition n'est pas vérifiée: exécution d'une commande "echo" suivi de l'execution de la commande "./gufo_run.native".
  9. fermeture des portés de variables ouvertes.

Divers

Il y aurait de nombreuses autres fonctionnalités à montrer, elles seront présentés dans un tutoriel plus complet (en particulier, concernant les tuples, listes, set et map...). Voila simplement un dernier exemple pour expliquer qu'il y a un système de type "personnalisé", semblable au struct du C:

code

struct $mystruct= {
  var1: string ,
  var2: cmd,
  var3: $myotherstruct,
  var4: string -> int,
}

explication On a défini un type '$mystruct' disposant de quatre champs nommés et respectivement typés. Le champs "var3" a pour type une autre structure, le champs "var4" à pour type une fonction prenant une chaine de caractère et retournant un entier.

Une description technique

La description ici faîtes n'est que très partielle et est voué à être détaillé à l'avenir.

Gufo est codé en Ocaml et fait environs 5000 lignes. Il repose beaucoup sur les mécanismes d'ocaml (type (le int de Gufo est strictement le int d'ocaml, gestion de la mémoire...) mais ajoute le mécanisme de commande externe, de set et map sans passer par des foncteurs et rend plus facile (d'avantage "script") son utilisation. Il ne cherche pas atteindre ces mécanismes avancés.

Le code est parsé puis est transformé dans une représentation intermédiaire optimisée (tous les symboles sont remplacés par des entiers ce qui permet des gains importants de performances).

Gufo est un language fonctionnel et si il permettra quelques astuces impérative, le restera.

Image of the core

Image of the core

L'état du projet

Mon objectif était d'avancer au maximum le projet avant de le présenter pour pouvoir donner autant de précision et de garantie que possible sur les qualité du langage. Le projet tout de même apparaît bien ambitieux et je ne peux pas d'avantage le développer sans soutien financier. J'ai donc décidé de faire cette présentation, pour présenter honnêtement le projet, vous permettre de l'évaluer et de considérer si il vous paraissais utile de l'aider. Votre avis me sera utile pour savoir si le projet semble avoir du sens.

Votre aide, cela peut être des remarques, des conseils, et également des perspectives de financement si le projet vous semble pertinent. Selon les réactions, j'organiserais une campagne de financement.

Je tiens à préciser que quoi qu'il arrive, le logiciel est destiné, si il est publié à être publié sous licence libre. Pour autant, je considère que tout travail (utile) mérite salaire: la qualité de la publication dépendra de l'aide reçu.

Pour l'instant, je souhaite en être le seul développeur, jusqu'à cette première publication éventuelle. Ensuite, nous verrons la situation, mais comme indiqué, le projet sera libre.

Voici les nombreux points qui devraient être travaillés pour envisager une version "aboutie":

Pour conclure, j'ai eu, en tant qu'ingénieur en informatique, plusieurs fois l'opportunité de travailler avec des chercheurs, Je n'aurais certainement pas eu l'imagination ni les moyens de me lancer dans un tel projet sans ces expériences. Aussi, je tiens à leur exprimer mes remerciements.