J'avais déjà fait un truc similaire en Perl, c'était tout bonnement inutilisable. Au bout de 20 minutes GCC avait toujours pas compilé un fichier dont l'équivalent BF était de quelques centaine de caractères signifiants :/ .
Honnêtement j'ai juste lu les deux premières pages et les deux dernières, et je n'ai pas vu dans celles-là un interpreteteur en Qt.
Le mien date des débuts de l'apparition du cours Qt sur le sdz (environ un an). j'ai découvert l'utilité des piles, et je me suis arreté dès que l'interpreteur marchait, pas essayé d'implémenter un compilateur après. Je n'y ai pas retouché, et il doit surement pouvoir être amélioré. (De plus code non commenté (beurk) )
Il possède : une fenêtre type éditeur texte basique dans lequel rentrer son code, de quoi sauvegarder et charger un fichier, et un interpreteur, avec sortie ASCII (sortie de base dans la console) et sortie numérique (la valeur même du contenu de l'octet), ainsi qu'une execution pas à pas
#ifndef CONSTANTES
#define CONSTANTES
const int nb_cases = 10;
const int octet = 255;
const int next = '>';
const int prev = '<';
const int plus = '+';
const int moins = '-';
const int ask = ',';
const int give = '.';
const int debBoucle = '[';
const int finBoucle = ']';
#endif
fonctions.h
#ifndef __FONCTIONS_H__
#define __FONCTIONS_H__
#include <QApplication>
#include <QFile>
#include <QMessageBox>
#include <QStack>
#include "fenetre.h"
#include "question.h"
#include "constantes.h"
#include "fenetrepap.h"
//void launch(MaFenetre* fenetre,bool pap); //Execute le contenu du textEdit
//void analyseText(QString texte, int &i);
//int* ConstrucInstruction(QString texte , int &j , int** boucles , int nb_boucles);
//void exeInstruction(Ma Fenetre* fenetre , int** instructions , bool pap);
void incr(int &x, int n); //incremente x dans Z/nZ
void decr(int &x , int n ); //decremente x dans Z/nZ
int enter();
void alerte(int n);
void alerte(QString texte);
void exeInstruction(MaFenetre* fenetre, bool pap);
#endif // __FONCTIONS_H__
fonctions.cpp
#include "fonctions.h"
void incr(int &x , int n ){
x+=1;
if (x>=n) {
x -= n;
}
}
void decr(int &x , int n ){
x-=1;
if (x<0) {
x += n;
}
}
void alerte(int n){
switch(n)
{
case 1:
QMessageBox::warning(0,"Fermeture de boucle non ouverte","Une boucle a été fermée sans avoir été ouverte");
break;
case 2:
QMessageBox::warning(0,"Boucle non fermée","Une boucle n'a pas été fermée");
}
}
void alerte(QString texte){
QMessageBox::warning(0,"Attention !!",texte);
}
void exeInstruction(MaFenetre* fenetre, bool pap){
QString texte = fenetre -> Text();
int x=0; //position du pointeur
int* tableau = new int[nb_cases];
int size = texte.size();
int j;//Variable permettant de garder une trace de i, meme lorsqu'on modifie i (voir boucles)
for(x=0;x<nb_cases ; x++){
tableau[x] = 0;
}
x=0;
fenetre->reset();
QStack<int> pile;
for(int i = 0; i < size; i++){
bool efficace = true;
j = i;//on sauvegarde i au cas où
if (texte[i] == QChar(plus)){
incr(tableau[x],octet);
}else if (texte[i] == QChar(moins)){
decr(tableau[x],octet);
}else if (texte[i] == QChar(next)){
incr(x,nb_cases);
}else if (texte[i] == QChar(prev)){
decr(x,nb_cases);
}else if (texte[i] == QChar(ask)){
tableau[x]=enter();
}else if (texte[i] == QChar(give)){
fenetre->give(tableau[x]);
}else if (texte[i] == QChar(debBoucle)){
pile.push(i);
}else if (texte[i] == QChar(finBoucle)){
if ( pile.isEmpty() ) {
alerte(2);
break;
}else{
int k = pile.pop();
if (tableau[x] != 0){
i = k - 1;
}
}
}else{
efficace = false;
}
if (pap && efficace){
Fenetrepap fenetrepap(tableau[x]);
fenetrepap.show();
QPoint pos = fenetre->pos();
fenetrepap.move(pos.x()+390,pos.y()+210);
fenetre->surligner(j);
pap = fenetrepap.exec();
}
}
if (pile.size()!=0) {
alerte(1);
}
}
int enter(){
Dialog dlg;
dlg.show();
return dlg.exec();
}
Honnêtement j'ai juste lu les deux premières pages et les deux dernières, et je n'ai pas vu dans celles-là un interpreteteur en Qt.
Euh. Gné. Pardon ? Qt c'est pas une bibliothèque graphique ? Ce que tu as fait c'est un semblant d'IDE avec un interpréteur dedans. Nous on s'en fiche, ce qui nous intéresse c'est l'interpréteur lui-même, on a déjà un éditeur.
je suis vraiment surpris par le brainfuck, j'en avais déjà entendu parler, mais ca peut être un bon langage
Disons que c'est rapide comme interpréteur !
Euh. Gné. Pardon ? Qt c'est pas une bibliothèque graphique ? Ce que tu as fait c'est un semblant d'IDE avec un interpréteur dedans. Nous on s'en fiche, ce qui nous intéresse c'est l'interpréteur lui-même, on a déjà un éditeur.
Oui je sais Qt est une bibliothèque, mais elle est si complète, et je m'en suis servi de manière si cochonne qu'on ne peut pas dire que je l'ai écris en C++.
Sinon comme je l'ai précisé j'ai écris ce programme avant la création du topic, pour répondre a un besoin personnel, puisque je cherchais a faire des opérations arithmétiques élémentaires.
Aniem : Non mais tu comprends pas, Qt sert juste pour ce qui est affichage (y'a aussi des trucs pour le réseau dedans IIRC mais bref), pas pour coder l'interpréteur en lui-même.
Aniem : Non mais tu comprends pas, Qt sert juste pour ce qui est affichage (y'a aussi des trucs pour le réseau dedans IIRC mais bref), pas pour coder l'interpréteur en lui-même.
Non, non, on peut faire plus qu'afficher avec Qt.
Qt est vraiment une librairie à tout faire.
Si le code était moins moche (Aniem a effectivement fait un truc de cochon), les fonctions du cœur du programme, qui serviraient par exemple à passer à l'état suivant de l'interpréteur BF, seraient indépendantes de Qt. Ici, les instructions pour afficher sont mélangées au reste. Et à part une QStack qui traine (qu'on pourrait aisément remplacer par un objet de la STL), tout ce à quoi sert Qt c'est afficher ici.
Si le code était moins moche (Aniem a effectivement fait un truc de cochon), les fonctions du cœur du programme, qui serviraient par exemple à passer à l'état suivant de l'interpréteur BF, seraient indépendantes de Qt. Ici, les instructions pour afficher sont mélangées au reste. Et à part une QStack qui traine (qu'on pourrait aisément remplacer par un objet de la STL), tout ce à quoi sert Qt c'est afficher ici.
Je suis d'accord avec toi, mais je tenais juste à appuyer sur le fait que Qt peut faire bien plus que l'affichage.
Bonsoir les gens !
Désolé de remonter un topic aussi vieux...
Voici ma contribution en Ada
Le programme n'est pas encore parfait, mais qui sait, peut-être qu'un jour...
with Ada.Text_Io; use Ada.Text_Io;
with Ada.Command_Line; use Ada.Command_Line;
procedure bfi is
source : File_Type;
val : Character;
l : Integer := 0;
p : array (0..999999) of Character; -- Tableau des instructions
pc : Integer := 0; -- "Pointeur" sur l'instruction actuelle
pl : Integer := 0; -- Nombres d'instructions
x : array (0..29999) of Integer := (others=>0); -- Ruban de la mémoire
xc : Integer := 0; -- "Pointeur" sur la case mémoire actuelle
begin
if (Argument_Count = 1) then -- Bon nombre d'arguments ?
Open(source, In_File, Argument(1)); -- Ouverture du fichier
-- Copie de la source en mémoire
while (not End_Of_File(source)) loop
Get(source, val); -- Lecture d'un caractère
if (val = '+' or val = '-' or val = '.' or val = ',' or val = '>'
or val = '<' or val = '[' or val = ']') then -- Filtre pour récupérer instructions
p(pl) := val;
pl := pl + 1;
end if;
end loop;
Close(source); -- Fermeture du fichier
-- Tant qu'il y a des instructions à traiter...
while (pc < pl) loop
if (p(pc) = '+') then -- Incrémente la case mémoire
x(xc) := x(xc) + 1;
elsif (p(pc) = '-') then -- Décrémente la case mémoire
x(xc) := x(xc) - 1;
elsif (p(pc) = '.') then -- Affichage du contenu la case mémoire
Put(Character'Val(x(xc)));
elsif (p(pc) = ',') then -- Met une valeur dans la case mémoire
Put(">");
Get(val);
x(xc) := Character'Pos(val);
elsif (p(pc) = '>') then -- Avance d'une case mémoire
xc := xc + 1;
elsif (p(pc) = '<') then -- Recule d'une case mémoire
xc := xc - 1;
elsif (p(pc) = '[') then
if (x(xc) = 0) then
pc := pc + 1;
while (l > 0 or p(pc) /= ']') loop
if (p(pc) = '[') then l := l + 1; end if;
if (p(pc) = ']') then l := l - 1; end if;
pc := pc + 1;
end loop;
end if;
elsif (p(pc) = ']') then
pc := pc - 1;
while (l > 0 or p(pc) /= '[') loop
if (p(pc) = '[') then l := l - 1; end if;
if (p(pc) = ']') then l := l + 1; end if;
pc := pc - 1;
end loop;
pc := pc - 1;
end if;
pc := pc + 1;
end loop;
else
-- Affichage de l'aide
Put_Line("Usage :");
Put_Line(" bfi <fichier source>");
end if;
end bfi;
C'était pas la peine d'inclure la licence et les méta-données qui font 1/3 du fichier :/. Et je trouve que tu commentes un peu trop :
if (Argument_Count = 1) then -- Bon nombre d'arguments ?
Open(source, In_File, Argument(1)); -- Ouverture du fichier
-- Ces commentaires par exemples ne sont pas utiles
(Il faudra qu'un dév du SdZ m'explique d'où sort ce tri arbitraire des langages dans la liste de <code>.)
Après la forme, le fond.
Ta gestion des instructions est classique, ok. Cependant, ta gestion des boucles ([]) n'est pas très efficace ; est-ce que tu ne peux pas essayer de trouver un moyen de ne pas devoir reparcourir la liste d'instructions à chaque tour de boucle ? Un indice : pile. Tu peux t'inspirer des autres interpréteurs présents ici .
Autre question : tu dis vouloir passer par une liste doublement chaînée. Pourquoi ? Un entier (modulo la taille de ta mémoire si tu veux boucler) pour stocker la position dans un tableau ne coûte pas cher, comparé aux deux pointeurs que tu vas devoir utiliser pour chaque élément.
C'était pas la peine d'inclure la licence et les méta-données qui font 1/3 du fichier :/. Et je trouve que tu commentes un peu trop :
if (Argument_Count = 1) then -- Bon nombre d'arguments ?
Open(source, In_File, Argument(1)); -- Ouverture du fichier
-- Ces commentaires par exemples ne sont pas utiles
(Il faudra qu'un dév du SdZ m'explique d'où sort ce tri arbitraire des langages dans la liste de <code>.)
Après la forme, le fond.
Ta gestion des instructions est classique, ok. Cependant, ta gestion des boucles ([]) n'est pas très efficace ; est-ce que tu ne peux pas essayer de trouver un moyen de ne pas devoir reparcourir la liste d'instructions à chaque tour de boucle ? Un indice : pile. Tu peux t'inspirer des autres interpréteurs présents ici .
Autre question : tu dis vouloir passer par une liste doublement chaînée. Pourquoi ? Un entier (modulo la taille de ta mémoire si tu veux boucler) pour stocker la position dans un tableau ne coûte pas cher, comparé aux deux pointeurs que tu vas devoir utiliser pour chaque élément.
J'avais dit que c'était pas parfait
pour la licence est les métadonnées, j'ai juste fait un bête copié-collé de mon fichier de source, sans trop cherché à réfléchir.
Quand à la liste doublement chainée, c'est juste pour un entrainement, je me sers de ce programme pour mes révisions...
L'avantage de la liste doublement chaînée à la place d'un tableau de 30 000 char c'est qu'on peut jouer sur le wrapping.
(Quoiqu'on pourrait aussi le faire en remplaçant pointeur++; et pointeur--; par des ternaires.)
J'avais essayé d'implémenter un interprêteur de Brainfuck en C il y a pas longtemps après être tombé sur l'article de Wikipedia... Bon visiblement ça marche pas car il sort n'importe quoi quand on essaye d'exécuter un programme qui contient des boucles.
Le code en brainfuck est situé dans un fichier texte qui est ouvert avec fopen dans le main ; c'est le seul fichier ouvert et c'est lui qui est désigné par la variable 'fichier' passée à la plupart des fonctions.
Le pointeur sur le tableau de 30 000 char porte le nom original de 'pointeur'. J'ai bien sûr stocké sa valeur initiale dans une constante du main pour pouvoir libérer la mémoire à la fin du programme.
J'utilise une pile (c'est-à-dire une structure lifo) pour stocker les positions (récupérées avec ftell) des ouvertures de boucle de façon à ne pas être dépourvu face à d'éventuelles boucles imbriquées.
Je n'ai rien fait pour gérer les cas où le code en Brainfuck est faux (c'est-à-dire si les suites de > & < et de [ & ] ne sont pas des mots de Dick) parce que c'est pas mon soucis principal (tant que ça marchera pas parfaitement dans des cas normaux, je me ficherai pas mal des cas d'erreur). Je pourrai toujours rajouter une fonction qui parcours une fois le fichier avant l'interprêtation, avec deux compteurs qui s'incrémentent ou se décrémentent en fonction des < > [ ], et renvoient une erreur lorsqu'ils deviennent négatif.
Je poste que la partie qui correspond aux boucles car traduire > par pointeur++; je trouve pas ça super intéressant.
Tout d'abord listes.h et listes.c qui permettent de gérer les piles :
#ifndef DEF_LISTES_H
#define DEF_LISTES_H
typedef struct Element Element;
struct Element
{
long valeur;
Element *precedent;
};
typedef Element* Pile;
void ajouter( Pile maPile, long nouvelleValeur ); // Ajoute un element sur la pile
long retirer( Pile maPile ); // Retire le premier element de la pile et renvoie sa valeur
void libererPile( Pile maPile ); // Applique free a tous les elements de la pile
long valeurCourante( Pile maPile ); // Renvoie la valeur du premier element de la pile
#endif
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "boucles.h"
#include "listes.h"
void ouvrirBoucle( char *pointeur, Pile debutsDeBoucle, FILE* fichier )
{
switch ( *pointeur )
{
case 0:
sauterBoucle( fichier );
break;
default:
noterPosition( debutsDeBoucle, fichier );
break;
}
}
void fermerBoucle( char *pointeur, Pile debutsDeBoucle, FILE* fichier )
{
//printf("on est dans fermerBoucle\n");
switch ( *pointeur )
{
case 0:
// printf("on veut retirer debutsDeBoucle\n");
retirer( debutsDeBoucle );
// printf("on a retire debutsDeBoucle\n");
break;
default:
//printf("on veut revenir au debut\n");
revenir( debutsDeBoucle, fichier );
//printf("on est revenu au debut\n");
break;
}
}
void sauterBoucle( FILE* fichier )
{
while ( fgetc(fichier) != ']' );
}
void noterPosition( Pile debutsDeBoucle, FILE* fichier )
{
long position = ftell(fichier);
ajouter( debutsDeBoucle, position);
}
void revenir( Pile debutsDeBoucle, FILE* fichier )
{
//printf("on est dans revenir\n");
long position = valeurCourante( debutsDeBoucle );
//printf("on a note la position\n");
fseek( fichier, position, SEEK_SET );
//printf("on a fait fseek\n");
}
Bon c'est peut-être pas très original (et surtout ça marche pas vraiment), mais à part cet usage des piles les seules solutions qui me sont venues à l'esprit c'était soit de reparcourir tout le fichier en comptant les [ (ce qui permettrait de remplacer la pile par un simple int, et de gérer facilement une erreur qui se traduiraient alors par une valeur négative du compteur), soit de revenir en arrière jusqu'à rencontrer un '[' ; seulement je sais avancer avec fgetc, mais pour reculer je vois guerre que fseek(fichier,-1,SEEK_CUR) et j'aime pas vraiment cette fonction.
Réécrire le code en C comme l'a fait floooder m'a aussi effleuré l'esprit pendant une fraction de seconde mais bon faut vraiment être tordu pour le faire
Parce qu'un atelier ne meurt jamais, voici un interpréteur en Haskell sans grande prétention. Il n'utilise pas l'entrée standard mais une entrée fournie en paramètre. Tout commentaire est le bienvenu, Haskell étant tout nouveau pour moi.
Hum j'ai rapidement lu le code, et j'ai quelques commentaires en vrac :
Si on regarder la fonction evalSuiteInstr, on peut reconnaitre un foldl:
evalSuiteInstr instrs etat = foldl (flip evalInstr) etat instrs
-- ou alors :
evalSuiteInstr = flip $ foldl (flip evalInstr)
Ensuite, tu as écris du code qui ressemblent à ça :
evalInstr Suiv etat = let d = droite etat in
if d == []
then etat { gauche = milieu etat : gauche etat,
milieu = 0 }
else etat { gauche = milieu etat : gauche etat,
milieu = head d,
droite = tail d }
C'est plus beau avec un case :
evalInstr Suiv etat = case droite etat of
[] -> etat { gauche = milieu etat:gauche etat
, milieu = 0}
d:ds -> etat { gauche = milieu etat : gauche etat
, milieu = d
, droite = ds}
Voire même :
evalInstr Suiv etat = case etat of
Etat { gauche = g, milieu = m, droite = []} ->
etat { gauche = m:g, milieu = 0 }
Etat { gauche = g, milieu = m, droite = d:ds} ->
etat { gauche = m:g, milieu = d, droite = ds}
J'allais aussi te dire d'utiliser "interact" pour la sortie mais en fait ça ne fonctionne pas tel quel. Il faudrait envoyer les caractères au fur et à mesure pour ça, ça demanderait un peu de changements (mais il n'y aurait pas besoin de ce (reverse . sortie) à la fin)
Sa mémoire est "infinie", et chaque case contient un entier signé (sur 8 octets il me semble).
function inc(mem, i)
mem[mem.p] = mem[mem.p] + 1
return i
end
function dec(mem, i)
mem[mem.p] = mem[mem.p] - 1
return i
end
function left(mem, i)
mem.p = mem.p - 1
mem[mem.p] = mem[mem.p] or 0
return i
end
function right(mem, i)
mem.p = mem.p + 1
mem[mem.p] = mem[mem.p] or 0
return i
end
function read(mem, i)
mem[mem.p] = string.byte(io.read(1))
return i
end
function write(mem, i)
io.write(string.char(mem[mem.p]))
return i
end
function open(mem, i)
table.insert(mem.stack, i-1)
return i
end
function close(mem, i)
if mem[mem.p] ~= 0 then
return table.remove(mem.stack)
else
return i
end
end
function eval(code)
local len = string.len(code)
local ins = {["+"] = inc, ["-"] = dec, ["<"] = left, [">"] = right,
["["] = open, ["]"] = close, ["."] = write, [","] = read}
local mem = {0 ; p = 1, stack = {}}
local i = 1
while i <= len do
local c = string.sub(code, i, i)
if ins[c] then
i = ins[c](mem, i)
end
i = i + 1
end
end
-- MAIN
eval(io.read("*all"))
Il me semble tout à fait fonctionnel, et standard.
- d'une part, tu dépiles seulement dans le cas où tu retournes au début de la boucle, donc dans l'autre cas la position du crochet ouvrant reste sur la pile. Cela gaspille de la mémoire d'une part, et d'autre part on peut récupérer cette position en rajoutant une fermeture : par exemple le programme "+[-]+]", qui est incorrect et devrait provoquer une erreur, boucle à l'infini car il saute du dernier ']' au '['. C'est un bug.
- D'autre part, ta stratégie en cas de retour au début est sous-optimale : tu dépiles, puis tu retournes sur le crochet ouvrant où tu rempiles, ce qui ne sert pas à grand chose (pile manipulée pour ne rien changer). Tu ferais mieux de ne pas dépiler, et d'aller à la position suivant celle du crochet (...+1). C'est une maladresse.
En corrigeant ces deux soucis, tu dépilerais seulement dans le cas où tu quittes la boucle (la valeur est nulle et tu continues la suite du programme), ce qui est plus logique.
Sinon, je n'ai jamais vu de code Lua, mais je trouve ça plutôt propre, avec l'utilisation des fonctions dans le tableau etc.
Bon, je corrige chez moi, mais je n'édite pas mon code ici.
Citation : bluestorm
Sinon, je n'ai jamais vu de code Lua
A vrai dire, moi non plus, sinon les miens. Je me débrouille avec la doc.
Les codes en Lua ne foisonnent pas sur le Web, du moins les codes qui n'ont aucun rapport avec la PSP ou la DS.
Mais du coup, j'ai peut-être un style très différent des autres programmeurs Lua...
En lisant l'autre topic sur l'atelier j'ai découvert celui ci, et ça tombe bien, j'ai justement codé un interpréteur Brainfuck pour PSP y'a quelques temps
C'est codé en C. En plus de l'interpréteur, il y a également un éditeur et un debugger (gestion des breakpoints, du pas à pas, etc)
Le code est déjà compilé, si vous avez une PSP débloquée pour lancer des homebrews, il vous suffit de créer un dossier dans ms0:/PSP/GAME/ et de copier dedans les fichiers suivants :
- EBOOT.PBP, l'executable
- config.txt, le fichier de config
- input.txt, le fichier d'entré (avec ",")
- script.bf, le script a executer
(j'ai testé sur CF 5.50 GEN, je ne sais pas si ça marche sur d'autres plus anciens mais c'est à essayer)
bonjour,
j'essaie de relever un piti challenge; celui-ci: brainfuck
voici mon code:
import sys
#C,X=sys.stdin.read().split('!')
C,X=',[>>++++++[-<+++++++>]<+<[->.<]>+++.<++++[->++++<]>.>,]!venom'.split('!')
X=iter(X)
out = ''
tab=d=0
D=[0]*99
for i in C:
if i==']':tab-=1
out+=tab*' '+'d+=1 d-=1 D[d]+=1 D[d]-=1 sys.stdout.write(chr(D[d])) D[d]=ord(X.next()) while(D[d]): pass pass'.split()['><+-.,[]\n'.index(i)]+'\n'
if i=='[':tab+=1
exec(out)
la sortie attendue est correcte,sauf pour les run4,5,6 où en plus de la sortie j'ai un "StopIteration" qui invalide le test.
je cherche, mais je ne trouve pas où est l'erreur.
une pitite idée ?
Pour le 4 tu peux rajouter un "\0" à (X = iter(X+"\0") ) pour un arrêt correcte du code, les deux suivants sont mal conçus et n'en finissent jamais de demander une entrée.
try:
exec(out)
except :
pass
Permet de forcer silencieusement l'arrêt du programme quand il n'y a plus d'entrée, la meilleure chose à faire ici sans doute.
merci EMC1,
je doutais que c'était un problème de 'point d'arrêt' et qu'il y avait un truc à faire avec le iter() car le try/except est trop lourd pour ce type de challenge.
je ne connaissais pas "\0", c'est trop cool ... merci beaucoup.
je vais pouvoir à présent travailler sur la taille du code.
Encore merci !
Bonjour,
après avoir découvert cet atelier je me suis mis en tête de faire un compilateur brainfuck pour linux (et BSD - en tout cas FreeBSD - mais je ne l'ai pas encore testé).
J'ai ces fonctionnalités :
- prend le code brainfuck sur l'entrée standard et écrit du code assembleur gas sur la sortie standard
- ne nécessite aucune librairie (même pas la libc)
- fonctionne sous linux et (peut-être) BSD sur processeurs x86
- mémoire de 30 000 octets circulaire
- "optimisation" : un code comme +++ n'effectue pas trois incrémentation mais ajoute trois à la case courante.
- buffering des entrées sorties
Mon code est tout à fait critiquable sur pas mal d'aspect (notamment certaines valeurs qui sont écrites plusieurs fois en dur au lieu d'être mises dans des defines, et pour la façon un peu gore dont est écrit le code asm), mais le code est, je l'espère, relativement compréhensible.
EDIT : le code que j'avais posté avait été tronqué, probablement trop long. On peut le trouver ici : http://tinypaste.com/96e42
C'est sympa (lastsseldon avait fait quelque chose de comparable au début du topic). Tu as fait des tests de performance par rapport à de bonnes implémentations interprétées ?
Maintenant oui et mon compilo vaut le coup ^^.
Sur ce programme qui affiche l'ensemble de mandelbrot en texte, et sur ma machine, mon compilateur donne un programme 4 fois plus rapide que l’interpréteur bf (packet ubuntu) et 10 fois plus rapide que l’interpréteur beef (packet ubuntu aussi).
Je pense qu'il serait intéressant aussi d'optimiser la mise a zéro de la case [-], même si le code de mon compilateur est pas facilement adaptable a ça...
EDIT : c'est maintenant fait, même si ça rend le code encore moins potable... Autre optimisation : la circularité de la mémoire est maintenant optionnelle. Le programme si dessus tourne a peu près 25% plus vite ainsi chez moi. http://tinypaste.com/361090
Par contre j'ai fait un autre test : j'ai pris le programme de Flooder, qui convertit le brainfuck en C, et je l'ai modifié pour que 1/ il fonctionne sous linux 2/ il ne gère plus la mémoire circulaire (qui le rendait extrêmement lent). Apres la demi-minute (!) de compilation sur gcc avec -O3, le programme résultant était 10% plus rapide que le mien... J'ai encore quelques idées d'optimisation mais je crois qu'il faut tout de même que je m'incline devant gcc...
Les moins du programme :
- Le code ASM généré n'est pas optimisé.
- Mémoire statique de 10ko uniquement.
- J'utilise system, et j'en suis pas fier...
- Partie du code peut être assez moches.
- Gestion des erreurs inexistante.
- Autres monstruosités?
Je cherche un moyen pour calculer la mémoire nécessaire au programme, sans l'interpréter. Quelqu'un a une idée?
Sinon, mon code est moins rapide que si je génère un code C, et que je le compile avec gcc.
Merci d'avance pour vos commentaires .
Voici le code ASM généré du "Hello World" de Wikipédia :
section .data
tab times 10000 db 0
section .text
global _start
my_putchar:
mov ecx, esi
mov ebx, 1
mov edx, 1
mov eax, 4
int 0x80
ret
my_getchar:
mov ecx, esi
mov ebx, 0
mov edx, 1
mov eax, 3
int 0x80
ret
_start:
enter 0,0
mov esi, tab
add byte[esi], 10
loop_1:
cmp byte[esi], 0
je near end_loop_1
add esi, 1
add byte[esi], 7
add esi, 1
add byte[esi], 10
add esi, 1
add byte[esi], 3
add esi, 1
add byte[esi], 1
sub esi, 4
sub byte[esi], 1
jmp loop_1
end_loop_1:
add esi, 1
add byte[esi], 2
call my_putchar
add esi, 1
add byte[esi], 1
call my_putchar
add byte[esi], 7
call my_putchar
call my_putchar
add byte[esi], 3
call my_putchar
add esi, 1
add byte[esi], 2
call my_putchar
sub esi, 2
add byte[esi], 15
call my_putchar
add esi, 1
call my_putchar
add byte[esi], 3
call my_putchar
sub byte[esi], 6
call my_putchar
sub byte[esi], 8
call my_putchar
add esi, 1
add byte[esi], 1
call my_putchar
add esi, 1
call my_putchar
mov eax, 1
mov ebx, 0x0
int 0x80
Tiens, aurais-je fait des émules ?
Je trouve qu'on a un code (et des résultats) assez similaires. Le perl te permet de faire un truc plus court et donc moins gore sur certains aspects.
Je pense que mon programme devrait utiliser comme toi une mémoire statique au lieu d'allouer sur la pile, ça éviterait d'avoir a initialiser la mémoire au début.
Par contre il y a quelque chose que je ne comprends pas : pourquoi faire des push/pop à chaque appel de fonction alors que ecx contient déjà la bonne adresse ? Pas besoin de suivre les conventions d'appel C à mon avis (même problème pour la sauvegarde du frame pointeur : inutile, même si ça rend le débuggage du code final dans gdb plus facile).
Pour ce qui est de ta question sur la mémoire, je pense que ne peux tout simplement pas le faire sans l'interpréter, ou alors que dans des cas triviaux (tout les > et les < sont en dehors des boucles, par exemple). De plus, même en interprétant le programme, tu ne fera qu'augmenter le nombre de cas triviaux que tu pourra traiter (programmes sans input ou ne pouvant recevoir au maximum qu'un nombre finit de caractères - encore faut-il déterminer avec certitude ce nombre, ce qui pose le même type de problème).
Pour certains programmes il n'y a tout simplement pas de limite, exemple :
Ce programme inverse les caracteres du fichier passe en entree
+[>,]<[-[+.<-]]
Dans ce cas la mémoire utilisée dépend de la taille de l’entrée, et tu ne peux donc pas la déterminer a l'avance.
Au final ce problème s'apparente au problème de l’arrêt, qui est indécidable.
[Atelier tous langages] Codez un interpréteur de Brainfuck
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.