Programmation Factor

A la découverte de Factor

Ce premier tutoriel s'inspire fortement de celui publié par ElasticDog. J'ai tout de même tâché d'apporter ma pierre à l'édifice en modifiant certaines parties du document initial et en créant des diagrammes bien plus beaux que les siens :)

A la découverte de Factor
Présentation
Petits rappels
Mais que peut-on donc faire avec une pile ?
La prochaine fois...

Présentation

Factor est un langage assez récent (il fête en 2009 sa sixième année de développement), néanmoins les concepts qu'il embarque ne le sont pas tant que cela, puisque c'est un héritier de Forth, le célèbre langage à pile (je dis célèbre, mais peut-être que PostScript le sera plus pour certains d'entre vous). Il a été développé par Slava Petrov, auteur du fameux éditeur de texte JEdit.

La page d'acceuil de Factor en dit peu sur le langage : innovatif, expressif et rapide sont les divers qualificatifs qui lui sont attribués, et nous allons tâcher ici de comprendre en quoi.

Je ne détaillerai pas ici l'installation de Factor, celle-ci se faisant toute seule à partir de la page d'acceuil.

Autre point, pour ce tutoriel uniquement, il ne sera pas nécessaire d'utiliser un quelconque éditeur de texte pour vos scripts, puisque nous allons travailler uniquement avec le listener (la console fournie d'office avec Factor).

Cependant, nous en utiliserons un dans les prochains tutoriels. Je ne saurai trop vous conseiller le mode FUEL de EMacs, il est extraordinaire (très semblable au mode SLIME pour les utilisateurs de Common Lisp). Il existe aussi des modes pour les éditeurs classiques (TextMate, NotePad++, JEdit, etc.), mais ils sont beaucoup moins poussés que ce que propose FUEL.

Petits rappels

Le temps où vous faisiez des maths est peut-être lointain, néanmoins nous allons rapeller ici certains concepts sur les notations mathématiques. Et rassurez-vous, on n'ira pas plus loin que l'addition en étudiant "2+3" !

Infix

C'est celle que nous utilisons tous les jours. Le IN de Infix signifie "à l'intérieur" ou "dedans". Mais à l'intérieur de quoi me direz-vous ? On parle en fait ici de l'opérateur (ici le "+") que vous utilisez, et il doit se placer à l'intérieur des opérandes (ici les nombres).

2 + 3

Prefix

PRE signifie "avant", ainsi l'opérateur s'écrit avant les opérandes. Les langages Lisp et Scheme l'utilisent.

+ 2 3

Postfix

POST signifie "après", et si vous avez bien suivi jusqu'à maintenant, vous écrirez les yeux fermés:

2 3 +

Si vous n'êtes pas tout jeune, vous avez peut-être déjà utilisé cette notation (dite "polonaise inversée") lorsque vous planchiez sur un DS de maths au lycée avec une calculatrice HP.

C'est cette syntaxe qui sera utilisée par la suite avec Factor.

Chacune des ces notations possède ses avantages et inconvénients. Avec la notation infixée, on doit connaître au préalable les priorités ou bien placer correctement des parenthèses. En notation postfixée, le fait que la multiplication soit traitée en premier devient implicite. Enfin, en préfixée, la multiplication est encore implicite mais devient vite complexe à lire lorsque l'on commence à imbriquer les opérations. Notez toutefois que la notation traditionnelle en mathématiques les utilise toutes les trois, et c'est bien là le noeud du problème lorsqu'il s'agit de parser de telles expressions.

Par ex. 2+3 est la notation infixée, sin est une notation préfixée, la dérivation f' pour une fonction f dérivable sur un domaine donné est une notation postfixée.

Mais que peut-on donc faire avec une pile ?

Le concept de "pile" en informatique est loin d'être nouveau. Pensez par exemple à la pile d'assiettes que vous avez encore en ce moment même sur votre évier!

C'est une structure de données dite "dernier entré, premier sorti" dans laquelle on peut ajouter un élément avec l'instruction push ou bien en retirer un avec pop .

Voici un magnifique schéma ASCII pour illustrer mes propos. Supposons que notre pile possède les éléments a,b et c (c étant le dernier entré) et que nous voulions lui ajouter un élément d. On utilisera pour cela l'instruction push ainsi: "d push" (et non "push d", rapellez-vous la notation postfixée!) :

|   |           | d |
| c |           | c |
| b | --------> | b |
| a |  d push   | a |
|___|           |___|

Maintenant, on va retirer le dernier élément de la pile avec pop:

| d |           |   |
| c |           | c |
| b | --------> | b |
| a |    pop    | a |
|___|           |___|

Bien sûr, pour le moment a,b,c et d ne représentent que des entités imaginaires, et comme dit la pub:

C'est à vous d'inventer la vie qui va avec!

Comment Factor utilise la pile ?

En fait, seules deux possibilités s'offrent à vous (en réalité, c'est un poil plus complexe que cela) :

  1. Soit vous entrez un élément sur la pile;
  2. Soit vous appelez un mot (tout simplement une fonction) qui va alors consummer des éléments de la pile.

Etudions ensemble un petit exemple avec le listener de Factor. C'est le traditionnel "Hello world!" :

(scratchpad) "Hello world!" print
Hello world!

Tout d'abord, on pousse l'élément "Hello world!" sur la pile, puis le mot print. Lorsque Factor rencontre un mot sur la pile, il éxécute celui-ci.

Afin de comprendre comment fonctionnent ces mots, nous allons utiliser la documentation fournie avec Factor. Pour cela, appuyez sur le bouton Search du listener. Dans la zone de recherche, entrez "print" puis cliquez sur la ligne "print ( string — )".

Nous sommes alors redirigés vers la documentation de print et la première ligne est:

print ( string -- )

Ceci signifie que print prend un seul argument en entrée (ce qui se trouve devant le symbole "—"), de type string , et que le mot ne renvoie rien (ce qui se trouve derrière le symbole "—" ).

La doc nous renseigne aussi sur le vocabulaire auquel appartient ce mot, et sur le rôle de celui-ci: ici, on imprime une chaîne à l'écran suivi d'une nouvelle ligne.

Pour voir le processus d'encore plus près, on peut entrer les instructions les unes après les autres dans la pile:

(scratchpad) "Hello world!"

--- Data stack:
"Hello world!"
(scratchpad) print
Hello world!

On peut déjà appliquer ce principe à de petits calculs arithmétique simples:

(scratchpad) 2 3

--- Data stack:
2
3

Remarquez que la pile est affichée la tête en bas dans le listener, et ainsi le nombre "2" est en fait en bas de la pile.

Si on désire ajouter ces deux nombres, on va utiliser le mot + :

(scratchpad) +

--- Data stack:
5

Quelques mots supplémentaires

Il y a de grandes chances que votre pile devienne un amas de données immaintenable si vous continuez à amasser des éléments à l'intérieur.

Voici quelques mots élémentaires pour vous aider dans son maniement:

Exemples

(scratchpad) 2 3 -

--- Data stack:
-1
(scratchpad) drop
(scratchpad) 20 5 / .
4
(scratchpad) 2 3 + 6 7

--- Data stack:
5
6
7
(scratchpad) clear
(scratchpad)

Nos premiers mots

Commençons par écrire un mot tout simple qui nous permettra de doubler l'argument qui lui est donné en entrée:

( scratchpad ) : double-du-nombre ( nombre -- sondouble )
2 * ;
( scratchpad ) 5 double-du-nombre .
10
La syntaxe

Reprenons la définition de notre mot :

: double-du-nombre ( nombre -- sondouble )
    2 * ;

: double-du-nombre ( nombre -- sondouble )
    nombre 2 * ;

Vous obtiendriez alors l'erreur suivante:

Word not found in current vocabulary search path
"name" "nombre"

vous précisant que le mot n'est pas défini!

Avec Factor, toutes les instructions doivent être séparées par des espaces, car tout est un mot. Les point-virgule, deux-points, les parenthèses, etc. sont en fait des mots placés ensemble pour former la syntaxe du langage! En cela, le concept "code is data" tant vanté par les programmeurs des dialectes de Lisp est aussi vrai avec Factor. (NDLR: et les macros ? Oui, elles sont là!)

La prochaine fois...

Nous irons bien entendu plus loin, en explorant les stack-shufflers, les quotations et les combinators. Mais il s'agissait surtout de présenter Factor et sa syntaxe qui sont si particuliers.

En attendant, n'hésitez pas à aller faire un tour dans la documentation, et à faire vos pas en écrivant vos premiers mots!

Juste à titre d'exercice, essayer d'implémenter les mots suivants :