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 :)
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.
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" !
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
PRE signifie "avant", ainsi l'opérateur s'écrit avant les opérandes. Les langages Lisp et Scheme l'utilisent.
+ 2 3
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.
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!
En fait, seules deux possibilités s'offrent à vous (en réalité, c'est un poil plus complexe que cela) :
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
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:
drop ( x -- )
: enlève le dernier élément de la pile et le jette;. ( obj -- )
: affiche le dernier élément de la pile;clear ( -- )
: vide la pile;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)
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
Reprenons la définition de notre mot :
: double-du-nombre ( nombre -- sondouble ) 2 * ;
:
sont utilisés pour commencer la définition d'un mot.double-du-nombre
.
Comme vous pouvez le constater, Factor vous permet beaucoup de liberté quant
au nommage de ceux-ci, et beaucoup de caractères spéciaux et interdits dans les
définitions de fonctions dans d'autres langages sont ici permis. Si vous avez
déjà pratiqué Lisp ou un de ses dialectes, vous ne serez pas surpris cependant.
Sachez enfin qu'il existe des conventions de nommage, par ex. un mot qui renverra
un booléen (soit vrai, soit faux) se terminera par le point d'interrogation ?
:
est-pair? voyelle? etc.( nombre -- sondouble )
est ce que l'on apelle
la déclaration d'effet de pile
(stack effect en Anglais).
On la nomme aussi signature en langage informatique.
Celle-ci est formée de deux parties, séparées par le double signe moins --
.
Chacune d'elle est une liste d'arguments séparés par un espace.
Tous les mots que vous definirez devront renseigner cet effet de pile
, à moins qu'ils
n'accumulent des valeurs sur celle-ci. Le nom que vous donnerez à ces arguments
n'a que peu d'importance, ( elt elt -- seq )
ou ( x y -- z )
représente le même effet,
mais la première syntaxe est tout de même plus explicite.
Enfin, et c'est certainement là où j'avoue me prendre les pieds dans le tapis le plus souvent, ces argument ne sont pas utilisés à l'intérieur du corps de votre mot. Ainsi, notre mot précédent ne pourrait pas s'écrire ainsi :
: 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à!)
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 :