Programmation Brainfuck/Version imprimable
Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_Brainfuck
Introduction
Brainfuck est un langage de programmation minimaliste, inventé par Urban Müller en 1993. Le nom est anglais, il vient de la contraction de brain, cerveau, et fuck, niquer. Il a d'ailleurs d'autres noms comme Brainf*ck, Brainf*** ou encore BF. Auraient-ils honte ?
L'objectif de Müller était de créer un langage de programmation simple pour une machine de Turing qu'il pourrait implémenter avec le compilateur le plus petit possible. Le langage consiste en 8 instructions. La version 2 de son compilateur original, écrit pour l'Amiga, faisait 240 octets en taille.
Comme son nom ne le suggère pas pour un anglophobe, les programmes Brainfuck sont difficiles à comprendre, peut-être même dangereux pour la santé mentale des programmeurs. Cependant, une machine de Turing et donc Brainfuck, peut accomplir n'importe quelle tâche. En ne tenant pas compte de la difficulté apparente à programmer certaines tâches en Brainfuck, c'est certainement possible de le faire.
Le langage est basé sur un modèle de machine simple consistant en un tableau d'octets initialisés à 0, d'un pointeur sur le tableau (pointant sur le premier octet du tableau) et de 2 files d'octets pour les entrées et les sorties.
Les 8 instructions du langage
Les 8 instructions du langage, chacune codée par un caractère, sont les suivantes :
Caract. | Signification |
---|---|
> | incrémente (augmente de 1) le pointeur. |
< | décrémente (diminue de 1) le pointeur. |
+ | incrémente l'octet du tableau pointé par le pointeur (l'octet pointé). |
- | décrémente l'octet pointé. |
. | sortie de l'octet pointé (valeur ASCII). |
, | entrée d'un octet dans le tableau à l'endroit du pointeur (valeur ASCII). |
[ | saute à l'instruction après le ] correspondant si l'octet pointé est à 0. |
] | retourne à l'instruction après le [ si l'octet pointé est différent de 0. |
Remarques :
- Alternativement, ] peut être défini par "retourne au [ correspondant". C'est plus court, mais moins symétrique et moins efficace en temps. Les deux versions produisent des programmes avec le même comportement.
- Une troisième version équivalente, quoique moins considérée, est que [ signifie "saute après l'instruction ] correspondante", et ] signifie "retourne à l'instruction après le [ correspondant si l'octet pointé est différent de 0".
Commentaires
[modifier | modifier le wikicode]Tout autre caractère que les 8 correspondant aux instructions est ignoré et considéré comme un commentaire, ce qui inclut les lettres et les chiffres.
Compilateurs et interpréteurs
Compilation en C
[modifier | modifier le wikicode]Les programmes Brainfuck peuvent être traduits en C en utilisant les substitutions suivantes, en considérant que ptr
est du type char*
:
Brainfuck | C |
---|---|
> |
++ptr;
|
< |
--ptr;
|
+ |
++*ptr;
|
- |
--*ptr;
|
. |
putchar(*ptr);
|
, |
*ptr = getchar();
|
[ |
while (*ptr) {
|
] |
}
|
Le programme en C ci-dessous génère un code source en C à partir d'un programme source en Brainfuck. Cependant, le programme généré a une limite de taille mémoire, et le débordement de pointeur n'est pas vérifié.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define DEFAULT_DATA_SIZE 1024
void printHeader(unsigned int data_size)
{
printf("#include <stdio.h>\n");
printf("#include <stdlib.h>\n\n");
printf("unsigned char octets[%d];\n", data_size);
printf("unsigned char *ptr;\n\n");
printf("int main()\n");
printf("{\n");
printf("\tptr = octets + %d;\n", data_size/2); /* Pointeur au milieu */
return;
}
void printFooter()
{
printf("\n\treturn 0;\n}\n");
return;
}
int main()
{
unsigned int data_size = DEFAULT_DATA_SIZE;
int c;
printHeader(data_size);
while ((c = getchar()) != EOF)
{
switch(c)
{
case '>': printf("\tptr++;\n"); break;
case '<': printf("\tptr--;\n"); break;
case '+': printf("\t(*ptr)++;\n"); break;
case '-': printf("\t(*ptr)--;\n"); break;
case '.': printf("\tputchar(*ptr);\n"); break;
case ',': printf("\t*ptr = getchar();\n"); break;
case '[': printf("\twhile (*ptr)\n{\n"); break;
case ']': printf("\t}\n"); break;
}
}
printFooter();
return 0;
}
Interpréteur en Javascript
[modifier | modifier le wikicode]Le code source ci-dessous est un interpréteur du langage Brainfuck. Il effectue une précompilation facilitant l'interprétation du code, en particulier pour les sauts d'instruction pour éviter de rechercher le caractère crochet correspondant. Il a la particularité d'autoriser tous les mouvements de pointeur de données, en allouant et décalant le buffer de données quand nécessaire. La seule limite étant la capacité mémoire du système.
function SyntaxErrorException(message, index)
{
this.message = message;
this.index = index;
}
class Brainfuck
{
// Runtime data and input
bf_data = []; // Data area
bf_index = 0; // Data pointer
ip = 0; // Instruction Pointer (IP)
input=[]; // Input data
o_in = 0; // Offset in input buffer
// Compilation
target_ip = []; // Target IP for [ and ]
prog = ''; // Current program
constructor(prog, input)
{
if (input!==undefined) this.input = input;
this.compile(prog);
}
resetinput()
{
this.input = []
this.o_in = 0
}
addinput(input)
{
if (input!==undefined && input!==null)
{
if (Array.isArray(input))
this.input = this.input.concat(input)
else if (typeof(input)==='number')
this.input = this.input.concat([input])
else if (typeof(input)==='string')
this.input = this.input.concat(Object.assign([], input).map(v=>v.charCodeAt(0)))
}
}
compile(prog)
{
this.ip = 0;
this.prog = prog;
var stack = [];
var target_ip = [];
for(var i=0;i<prog.length;i++)
{
var c = prog.charAt(i);
switch(c)
{
case '[': stack.push(i); break;
case ']':
if (stack.length<=0) throw new SyntaxErrorException("ERROR ]", i);
var n = stack.pop();
target_ip[i] = n+1;
target_ip[n] = i+1;
break;
default: target_ip[i] = 0;
}
}
if (stack.length>0) throw new SyntaxErrorException("ERROR [", i);
this.target_ip = target_ip;
}
reset()
{
this.bf_data=[];
this.bf_index=0;
this.ip = 0;
}
make()
{
if (this.bf_index<0)
{
let ar = [];
while (this.bf_index<0) { ar.push(0); this.bf_index++; }
this.bf_data = ar.concat(this.bf_data);
}
else if (this.bf_index>=this.bf_data.length)
{
while (this.bf_index>=this.bf_data.length) this.bf_data.push(0);
}
}
get current()
{
this.make();
return this.bf_data[this.bf_index];
}
set current(value)
{
this.make();
this.bf_data[this.bf_index] = value;
}
execute(maxsteps)
{
if (maxsteps===undefined) maxsteps = 10000;
var output = [];
var ip = this.ip;
var target_ip = this.target_ip;
var prog = this.prog
while (ip<prog.length)
{
if (--maxsteps<0) break;
var c = prog.charAt(ip++);
switch(c)
{
case '[': if (this.current==0) ip=target_ip[ip-1]; break;
case ']': if (this.current!=0) ip=target_ip[ip-1]; break;
case '+': this.current = this.current + 1; break;
case '-': this.current = this.current - 1; break;
case '>': this.bf_index++; break;
case '<': this.bf_index--; break;
case ',': this.current = (this.o_in>=this.input.length) ? 0 : this.input[this.o_in++]; break;
case '.': output.push(this.current); break;
default: // ignore comment
}
}
this.ip = ip;
return output;
}
// Retourne la sortie sous forme d'une chaîne de caractères.
run(maxsteps)
{
return this.execute(maxsteps).map(v => String.fromCharCode(v)).join('')
}
// Retourne la sortie sous forme d'un tableau de nombres.
runnum(maxsteps)
{
return this.execute(maxsteps)
}
}
L'exemple ci-dessous montre comment utiliser la classe :
new Brainfuck('++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.').run()
// Retourne "Hello World!\n"
L'interpréteur permet de spécifier les données à lire, soit au constructeur, soit au fur et à mesure par exécution pas à pas :
// Boucle de 4 itérations (7 instructions par itération + 5 initiales)
var prog = new Brainfuck('++++[>,+.<-]');
prog.addinput([10,40]);
res = prog.runnum(19); // les 2 premières itérations
// Tableau de résultat : [11,41]
prog.addinput(20);
res = res.concat(prog.runnum(7)); // itération suivante
// Tableau de résultat : [11,41,21]
prog.addinput(50);
res = res.concat(prog.runnum(7)); // itération suivante
// Tableau de résultat : [11,41,21,51]
prog.addinput(90);
res = res.concat(prog.runnum(7)); // itération suivante ?
// Tableau de résultat : [11,41,21,51] inchangé car les 4 itérations sont passées.
prog.reset(); // Remise à zéro de l'exécution
res = res.concat(prog.runnum(12)); // initialisation et première itération
// Tableau de résultat : [11,41,21,51,91]
Optimisations
[modifier | modifier le wikicode]Certaines optimisations sont possibles :
- Regrouper plusieurs instructions d'incrémentation ou décrémentation
+ -
en une instruction de synthèse ajoutant directement la valeur voulue, - Même principe pour les instructions de déplacement du pointeur de données
< >
, - Supprimer certaines séquences d'instructions qui s'annulent :
+-
<>
- ...
- Supprimer les séquences entre crochets (boucle) quand il est certain que l'octet pointé vaut zéro (aucune itération) :
- Une boucle en début de programme, car l'octet pointé vaut initialement zéro ;
- Une boucle suivant immédiatement une autre paire de crochets, car à la sortie de la première boucle, l'octet pointé vaut zéro ;
[ __A__ ][ __B__ ]
→[ __A__ ]
- Une boucle incluse dans une autre paire de crochets sans opérations entre les crochets des deux boucles, car après que toutes les itérations ont été effectuées par la boucle interne, l'octet pointé vaut zéro ;
[[ __A__ ]]
→[ __A__ ]
- Pré-calculer la position du crochet correspondant de chaque paire de la boucle (comme dans l'interpréteur en Javascript de la section précédente),
- Pour un interpréteur : retirer tous les caractères ignorés (commentaire),
- Optimiser les séquences de déplacement et incrémentation/décrémentation en calculant les opérations effectuées sur chaque case au cours du déplacement.
- Exemple :
<+<+<+>-<-
simplifié :<+<<
- ...
Comment effectuer les opérations courantes
Hello World!
[modifier | modifier le wikicode]Un programme qui affiche "Hello World!" sur l'écran est :
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
Brainfuck est intéressant dans ce cas, un programme "Hello World!" n'est pas facile à écrire ! Et à lire !
Avec un commentaire :
Hello world
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
Ou avec des commentaires expliquant le code, en anglais :
[ This program prints "Hello World!" and a newline to the screen, its
length is 106 active command characters. [It is not the shortest.]
This loop is an "initial comment loop", a simple way of adding a comment
to a BF program such that you don't have to worry about any command
characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
ignored, the "[" and "]" characters just have to be balanced. This
loop and the commands it contains are ignored because the current cell
defaults to a value of 0; the 0 value causes this loop to be skipped.
]
++++++++ Set Cell #0 to 8
[
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
[ as the cell will be cleared by the loop
>++ Add 2 to Cell #2
>+++ Add 3 to Cell #3
>+++ Add 3 to Cell #4
>+ Add 1 to Cell #5
<<<<- Decrement the loop counter in Cell #1
] Loop until Cell #1 is zero; number of iterations is 4
>+ Add 1 to Cell #2
>+ Add 1 to Cell #3
>- Subtract 1 from Cell #4
>>+ Add 1 to Cell #6
[<] Move back to the first zero cell you find; this will
be Cell #1 which was cleared by the previous loop
<- Decrement the loop Counter in Cell #0
] Loop until Cell #0 is zero; number of iterations is 8
The result of this is:
Cell no : 0 1 2 3 4 5 6
Contents: 0 0 72 104 88 32 8
Pointer : ^
>>. Cell #2 has value 72 which is 'H'
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
+++++++..+++. Likewise for 'llo' from Cell #3
>>. Cell #5 is 32 for the space
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
<. Cell #3 was set to 'o' from the end of 'Hello'
+++.------.--------. Cell #3 for 'rl' and 'd'
>>+. Add 1 to Cell #5 gives us an exclamation point
>++. And finally a newline from Cell #6
Remise à zéro de l'octet pointé
[modifier | modifier le wikicode][-]
Tant que l'octet est différent de 0, on le décrémente. On arrête donc la boucle ([]) quand il est à 0.
Entrée/Sortie d'un caractère
[modifier | modifier le wikicode],.
Prend un caractère du clavier pour l'afficher à l'écran.
Boucle simple
[modifier | modifier le wikicode],[.,]
Une boucle qui prend une entrée du clavier et l'affiche à l'écran. Notez qu'on s'attend à avoir un 0 pour marquer la fin de l'entrée (les implémentations peuvent être différentes à ce niveau là).
Manipulation de pointeur
[modifier | modifier le wikicode]>,[.>,]
Une version améliorée de la boucle précédente dans laquelle on stocke les caractères entrés dans le tableau pour une future utilisation, en déplaçant le pointeur à chaque fois.
Addition
[modifier | modifier le wikicode]Ce code ajoute l'octet courant (en le détruisant, il sera à 0 à la fin) à l'octet suivant.
[->+<]
Soit Tableau[0] = 2 et Tableau[1] = 8, "[" débute la boucle, "-" et Tableau[0] = 1, ">" on pointe sur l'octet 1, "+" et Tableau[1] = 9, "]" on recommence. À la fin, on aura bien Tableau[0] = 0 ce qui arrête la boucle, et Tableau[1] = 10.
Instructions conditionnelles
[modifier | modifier le wikicode],----------[----------------------.,----------]
Ce programme prend un caractère minuscule en entrée et le met en majuscule. Pour arrêter, on tape la touche entrée (code 10 en Brainfuck dans la plupart des compilos).
Au début, on récupère le premier caractère (,) et on lui soustrait immédiatement 10 (10 fois -). Si l'utilisateur a tapé entrée, on a 0 dans l'octet pointé et l'instruction de boucle ([) sautera à la fin du programme. Si le caractère entré n'est pas 10, on assume qu'il est en minuscule et on entre dans la boucle, où on va lui soustraire le nombre 22 (22 fois -), ce qui va faire 32 en tout, et 32 est la différence en ASCII entre la lettre minuscule et la même lettre en majuscule.
On va donc l'afficher, puis on en récupère une nouvelle, et à nouveau on lui soustrait 10. Et on repart au début de la boucle. Si le caractère entrée est un Entrée (10), la boucle s'arrêtera comme on l'a déjà vu, sinon on continue.
Addition
[modifier | modifier le wikicode],>++++++[<-------->-],,[<+>-],<.>.
Ce programme additionne 2 nombres à un seul chiffre et affiche le résultat si celui-ci n'a aussi qu'un seul chiffre :
4+3
7
(Maintenant les choses vont être un peu plus compliquées. Nous allons nous référer aux octets du tableau ainsi : [0], [1], [2], etc.)
Le premier nombre est entré dans [0], et on lui soustrait 48 pour avoir sa valeur décimale (les codes ASCII pour les chiffres 0-9 sont 48-57). Cela est fait en mettant 6 dans [1] et en utilisant une boucle pour soustraire 8 de [0] autant de fois que dans [1], soit 6 x 8 = 48. C'est une méthode plus commode pour ajouter ou soustraire des grands nombres que de mettre 48 fois "-" dans le programme. Le code qui fait cela est :
>++++++[<-------->-]
>++++++
pour mettre 6 dans [1], puis on attaque la boucle, "<" pour revenir sur [0], on soustrait 8, ">" on repasse sur [1], qu'on décrémente et on retourne dans la boucle. On va bien l'exécuter 6 fois, jusqu'à ce que [1] soit à 0.
Ensuite, on récupère le signe plus qu'on met dans [1] ; puis le second chiffre qui va écraser le signe plus.
La boucle suivante [<+>-]
fait le vrai boulot, ajoutant le second nombre dans le premier et remettant à zéro [1].
À chaque boucle, il ajoute 1 dans [0] et retire 1 de [1] ainsi [1] va finir par être à 0 tout ce qui a été ajouté à [0] a été retiré de [1].
Ensuite la touche entrée est mise dans [1]. (Note : il n'y a eu aucun contrôle des entrées.)
Puis le pointeur est remis sur [0], qui est affiché ([0] est maintenant a + (b + 48), puisqu'on n'a pas corrigé b ; ce qui est identique à (a + b) + 48, qui est ce que l'on veut). Maintenant, le pointeur est ramené sur [1], qui contient la touche entrée ; que l'on affiche, et le boulot est fait.
Multiplication
[modifier | modifier le wikicode],>,,>++++++++[<------<------>>-]
<<[>[>+>+<<-]>>[<<+>>-]<<<-]
>>>++++++[<++++++++>-],<.>.
Comme le précédent, mais effectue une multiplication, pas une addition.
Le premier nombre est entré dans [0], l'astérisque et le deuxième nombre dans [1] et les 2 nombres sont corrigés en leur soustrayant 48 (notez qu'il n'y a qu'une seule boucle de 8 itérations pour soustraire 6 à chaque itération aux 2 nombres !).
Ensuite on entre dans la boucle de multiplication principale. L'idée de base est qu'à chaque boucle on soustrait 1 de [0] et on ajoute [1] dans le total cumulé gardé en [2] (3 * 2 = 2 + 2 + 2). En particulier : la première boucle cumule [1] dans [2] et [3], tout en remettant [1] à 0 (C'est la manière basique de dupliquer un nombre). La deuxième boucle remet [3] dans [1], en remettant à 0 [3]. Puis on décrémente [0] et on est à la fin de la boucle principale. À la sortie de cette boucle, [0] contient 0, [1] a encore le 2e nombre, et [2] a le produit des 2 nombres. (Si on veut garder le premier nombre, on peut l'ajouter dans [4] à chaque itération de la boucle principale, puis à la fin de déplacer la valeur de [4] dans [0].)
Exemple : 3 * 2
[0] | [1] | [2] | [3] |
3 | 2 | 0 | 0 |
1re boucle : >[>+>+<<-]
| |||
3 | 1 | 1 | 1 |
3 | 0 | 2 | 2 |
2e boucle : >>[<<+>>-]
| |||
3 | 1 | 2 | 1 |
3 | 2 | 2 | 0 |
Fin boucle princ : <<<-]
| |||
2 | 2 | 2 | 0 |
1re boucle : >[>+>+<<-]
| |||
2 | 1 | 3 | 1 |
2 | 0 | 4 | 2 |
2e boucle : >>[<<+>>-]
| |||
2 | 1 | 4 | 1 |
2 | 2 | 4 | 0 |
Fin boucle princ : <<<-]
| |||
1 | 2 | 4 | 0 |
1re boucle : >[>+>+<<-]
| |||
1 | 1 | 5 | 1 |
1 | 0 | 6 | 2 |
2e boucle : >>[<<+>>-]
| |||
1 | 1 | 6 | 1 |
1 | 2 | 6 | 0 |
Fin boucle princ : <<<-]
| |||
0 | 2 | 6 | 0 |
Ensuite, il ne reste plus qu'à ajouter 48 au produit, récupérer la touche entrée dans [3], et afficher le produit ASCII et l'entrée qu'on vient juste de stocker.
Variantes
Le langage Brainfuck a inspiré plusieurs variantes du langage en le modifiant de différentes façons :
- Codage différent pour les 8 instructions du langage,
- Extension du langage par ajout d'instructions,
- ...
Ook
[modifier | modifier le wikicode]Le langage Ook est une variante de brainfuck, Turing-complet, conçu pour être parfaitement lisible par un orang-outan, faisant référence au personnage du bibliothécaire de l'univers du Disque-monde de Terry Pratchett. Les instructions sont celles du langage Brainfuck codées par deux onomatopées "Ook" suivies chacune par une ponctuation parmi les 3 possibles : point, point d’interrogation, point d'exclamation.
Ook | Brainfuck | Signification |
---|---|---|
Ook. Ook? | > | incrémente (augmente de 1) le pointeur. |
Ook? Ook. | < | décrémente (diminue de 1) le pointeur. |
Ook. Ook. | + | incrémente l'octet du tableau sur lequel est positionné le pointeur (l'octet pointé). |
Ook! Ook! | - | décrémente l'octet pointé. |
Ook! Ook. | . | sortie de l'octet pointé (valeur ASCII). |
Ook. Ook! | , | entrée d'un octet dans le tableau à l'endroit où est positionné le pointeur (valeur ASCII). |
Ook! Ook? | [ | saute à l'instruction après le Ook? Ook! correspondant si l'octet pointé est à 0.
|
Ook? Ook! | ] | retourne à l'instruction après le Ook! Ook? si l'octet pointé est différent de 0.
|
Ook? Ook? | fin du programme. |
Il y a neuf possibilités : les 8 instructions, et l'instruction supplémentaire indiquant la fin du programme.
L'exemple du Hello World codé en Ook :
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook? Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook. Ook? Ook. Ook. Ook! Ook. Ook. Ook? Ook! Ook. Ook? Ook?
f*ckf*ck
[modifier | modifier le wikicode]Le langage f*ckf*ck[1] modifie le codage des instructions en les remplaçant par des mots de 4 lettres censurés, et permettant d'insérer des commentaires utilisant la syntaxe du langage C. Les 2ème et 3ème lettres des mots peuvent être remplacées par n'importe quel caractères ASCII.
f*ckf*ck | Brainfuck | Signification |
---|---|---|
f**k | > | incrémente (augmente de 1) le pointeur. |
s**g | < | décrémente (diminue de 1) le pointeur. |
b**b | + | incrémente l'octet du tableau sur lequel est positionné le pointeur (l'octet pointé). |
t**s | - | décrémente l'octet pointé. |
c**k | . | sortie de l'octet pointé (valeur ASCII). |
k**b | , | entrée d'un octet dans le tableau à l'endroit où est positionné le pointeur (valeur ASCII). |
a**e | [ | saute à l'instruction après le b**t correspondant si l'octet pointé est à 0.
|
b**t | ] | retourne à l'instruction après le a**e si l'octet pointé est différent de 0.
|
Signification particulière :
f*ckf*ck | Signification |
---|---|
! | Répéter la commande précédente. |
/* | Début de commentaire. |
*/ | Fin de commentaire. |
Spoon
[modifier | modifier le wikicode]Le langage spoon est équivalent au langage brainfuck mais avec des mots constitués de 0 et 1, utilisant un codage de Huffman.
Spoon | Brainfuck | Signification |
---|---|---|
010 | > | incrémente (augmente de 1) le pointeur. |
011 | < | décrémente (diminue de 1) le pointeur. |
1 | + | incrémente l'octet du tableau sur lequel est positionné le pointeur (l'octet pointé). |
000 | - | décrémente l'octet pointé. |
0010110 | , | entrée d'un octet dans le tableau à l'endroit où est positionné le pointeur (valeur ASCII). |
001010 | . | sortie de l'octet pointé (valeur ASCII). |
00100 | [ | saute à l'instruction après le 0011 correspondant si l'octet pointé est à 0. |
0011 | ] | retourne à l'instruction après le 00100 si l'octet pointé est différent de 0. |
Il possède deux instructions supplémentaires :
Spoon | Signification |
---|---|
00101110 | sortie de tout le tableau mémoire. |
00101111 | fin immédiate du programme. |
Hello world en spoon :
1 1 1 1 1 1 1 1 1 1 00100 010 1 1 1 1 1 1 1 010 1 1 1 1 1 1 1 1 1 1 010 1 1 1 010 1 011 011 011 011 000 0011 010 1 1 001010 010 1 001010 1 1 1 1 1 1 1 001010 001010 1 1 1 001010 010 1 1 001010 011 011 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 001010 010 001010 1 1 1 001010 000 000 000 000 000 000 001010 000 000 000 000 000 000 000 000 001010 010 1 001010 010 001010
Les espaces peuvent être supprimés. On obtient alors un fichier binaire (exécutable) décodable sans ambiguïté :
Hello world en spoon sans espace :
11111111110010001011111110101111111111010111010101101101101100000110101100101001010010101111111001010001010111001010010110010100110111111111111111110010100100010101110010100000000000000000000010100000000000000000000000000010100101001010010001010
SegFaultProg
[modifier | modifier le wikicode]Le langage Segfaultprog est une variante du brainfuck, enrichie, extensible, destinée à être exécutée en machine virtuelle, sur de petites architectures (AVR, PIC, MIPS...). Son développement a débuté en mai 2010. Ce langage propose une simplification de la syntaxe du brainfuck (par exemple, avec l'adressage direct, et en évitant les répétitions), et rétro-compatibilité avec celui-ci.
Les commandes peuvent être suivie d'un argument sous la forme d'un octet (un caractère), représenté dans le tableau ci-dessous sous cette forme : argument.
Commande | Description |
---|---|
> nb
|
Incrémente le pointeur de nb cases. |
< nb
|
Décrémente le pointeur de nb cases. |
* pos
|
Place le pointeur à la position pos. |
+ nb
|
Incrémente l'octet pointé de nb unités. |
- nb
|
Décrémente l'octet pointé de nb unités. |
[
|
Début de boucle, saute après le crochet fermant correspondant si l'octet pointé vaut zéro. |
]
|
Fin de boucle, retourne après le crochet ouvrant correspondant si l'octet pointé ne vaut pas zéro. |
.
|
Affiche la valeur de l'octet pointé. |
,
|
Demande la saisie de la valeur de l'octet pointé. |
Les quatre dernières instructions du tableau ci-dessus correspondent exactement à celles du brainfuck. Il y a deux versions de la variante qui diffèrent pour la spécification des arguments :
- SegFaultProg V1
- Chaque argument est donné sous la forme d'un caractère.
- SegFaultProg V2
- Chaque argument est optionnel (1 par défaut) et spécifié sous l'une des deux formes suivantes :
- Sous la forme d'un caractère, précédé de la lettre
c
(Caractère) ; - Sous la forme d'un nombre en décimal (ex : 7) ou en hexadécimal (ex : 0x2f) entre deux caractères dollar.
- Sous la forme d'un caractère, précédé de la lettre
Hello world en SegFaultProg V1 :
*A+H.[-]+e.+7..+3.*B+32.*A+8.-8.+3.[-]+d.
Hello World en SegFaultProg V2 :
*$0x00$+cH.-$3$.+$7$..>+&+$0x03$.>+c .>+$0x57$.<<.+$3$.<.-$0x08$.
Whitespace
[modifier | modifier le wikicode]Dans le langage Whitespace[2], les instructions sont plus nombreuses que dans le langage Brainfuck. Elles sont codés par des espaces, tabulations et retour à la ligne. Le code est donc invisible dans un texte.
Pi
[modifier | modifier le wikicode]Pi est un langage de programmation ésotérique[3] basé sur des erreurs de chiffre du nombre pi. Les 8 instructions du brainfuck sont codées par les chiffres de 0 à 8, en sautant le chiffre de pi correspondant à la position de l'instruction.
Si le chiffre est 8 ou 9 :
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
> | < | + | - | . | , | [ | ] |
Si le chiffre est 4 :
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
> | < | + | - | . | , | [ | ] |
Si le chiffre est correct, il est ignoré.
Exemple : Hello world
3.1415926535897932384226433232725028841271693992751358239749 245923072164062822089986780348053431120629821483865332223366 470908446395535832317223594083284831172502841037019385311052 596446029489529303219642288009756259134461214751482337817834 652812009021456285664234613486134553226482133236073603491413 737242870066363155841744815239239622292545917053643278925902 605113301301488204625213821429519415316024320522703627595939 530921261373219226137931021185580744023794627495273518257527 348912274384830113491298346743644406563430865213349463352247 3749030217386093370277033921317659317670238267581846066440
Références
[modifier | modifier le wikicode]
Conclusion
« | |
Pourquoi faire simple lorsque l'on peut faire compliqué ? | |
» | |
À part pour des raisons pédagogiques, ce langage n'est pas utilisable, car concurrencé par de nombreux langages plus faciles (C, BASIC), ou ayant d'autres qualités (Assembleur...).
Il montre cependant qu'une machine peut faire des opérations complexes avec peu d'instructions de base, comme les processeurs RISC (Reduced Instruction Set Computing en anglais) dont le nombre réduit d'instructions facilite la conception des circuits de décodage permettant de réduire le coût. Cependant, comme le montre ce livre, coder à partir de peu d'instructions élémentaires engendre plus de code, augmentant la quantité de mémoire nécessaire pour le stockage des programmes. Cette quantité accrue de mémoire signifie également plus d'accès mémoire pour lire les instructions, donc des performances moindres.
GFDL | Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture. |