ProgrammieraufgabenHier wird regelmäßig eine neue Programmieraufgabe gestellt, die dann gelöst werden soll und in Zusammenarbeit mit den Moderatoren auch besprochen werden kann.
Brainfuck Interpreter
Diskussion: Brainfuck Interpreter im Forum Programmieraufgaben, in der Kategorie Code Kitchen; Anzeige
Eingereicht von Scorn07:
Zitat:
Programmieraufgabe: Brainfuck Interpreter
Ich gehe mal davon aus, dass hier die meisten Brainfuck kennen. Für ...
Könnte man wahrscheinlich als Schummeln auffassen, aber naja. Das Ding wandelt bf-source anhand der Tabelle im wiki-Artikel in c-source um. Ich schau mal, dass ich noch einen "richtigen" Interpreter nachreiche.
bf-src wird von stdin oder vom ersten Argument (dann als Dateiname) gelesen.
.186
CSEG segment
org 100h
Begin:
mov dx, offset Filename
mov ah,3dh ;al sollte per default 0 sein
int 21h
xchg bx,ax ;braucht nur 1 byte
;Speicher vorbereiten, jeweils 65k für BF Code und BF Array reservieren und Nullen
mov cx,cs
add cx,0ffffh/15 ;bisschen Platz für eigentlichen Com-code lassen
mov es,cx
xor cx,cx ;auf Anzahl der Bytes bringen
dec cx
;zeromemory , ax ist durch xchg bx,ax=0
rep stosb
;File lesen
xchg cx,di ;mov cx, di
; inc di ; di auf 0
mov ah,3fh
int 21h
mov si,dx ;nötig???
add dx,ax
;schließen (später löschen!)
mov ah,3eh
int 21h
mov bx,dx
;Nun SI ist der Zeiger auf Code
;DI zeiger auf Array
INTERPRET:
cld
lodsb
cmp al,'<'
jne @f
dec DI
@@:
cmp al,'>'
jne @f
inc DI
@@:
cmp al,'+'
jne @f
inc byte ptr es:[DI]
@@:
cmp al,'-'
jne @f
dec byte ptr es:[DI]
@@:
cmp al,'.'
jne @f
mov ah,02
mov dl,byte ptr es:[DI]
int 21h
@@:
cmp al,','
jne @f
mov ah,01
int 21h
mov byte ptr es:[DI],al
xchg cx,ax ;jmp ENDE CX ist i.R 0, also wird weitere ausführung nicht beeinflüsst
@@:
;[ jump forward to the command after the corresponding ] if the byte at the pointer is zero.
;] jump back to the command after the corresponding [ if the byte at the pointer is nonzero.
xor cx,cx
cmp byte ptr es:[DI],cl
jnz NEXT_BRC ;es ist nicht 0 , also interessiert [ Scan uns gar nicht, sonst:
cmp al,'[' ;ansonten ist [ und Zelle 0
je INC_
; oder nicht [ und 0, dann interessirt und auch der ] Fall nicht
jmp ENDE
NEXT_BRC:
cmp al,']';hier=>Zelle nicht 0 -> scannen nach [
jne ENDE
std
dec si
SCAN_LOOP:
lodsb
cmp al,'['
je INC_
cmp al,']'
jne SCAN_LOOP ;sonst ist es ] ->dec
dec cx
jz ENDE
jmp SCAN_LOOP
INC_: inc cx
jnz SCAN_LOOP ;wenn ungleich, kann weitergescannt werden
inc si ;korrigiere SI
inc si
ENDE:
cmp SI,bx
jbe INTERPRET
int 20h
filename db 'i',0
CSEG ends
end Begin
OS: DOSe (oder DOSen Emulator, wie z.B der von Windows ),
CPU: .186
Liest Programm aus der Datei 'I', maximale Programmgröße: 64K, genauso Arraygröße: 64K.
Getestet mit: HelloWorld, PI, Triangle, BottlesOfBeer, hanoi (53K Source).
Zum DOS Emu: zumindest unter Windows reicht der eingebaute - BF Source in eine Datei 'I' schreiben und die COM anklicken/per CMD starten.
Ist auf jedenfall noch einiges an Einsparpotential drin - alleine durch weglassen der Dateischließung und der korrekten Terminierung ließen sich 10 Bytes zusätzlich gewinnen. Dann könnte man noch die MemoryZerofunktion weglassen (und darauf vertrauen, dass beim Start des Interpreters der Speicher komplett leer ist) - diese Version brigt dann nur noch 122 Bytes auf die Waage. Sicherlich übersehe ich auch andere, optimierungsfähige Stellen - anderseits mache ich 16-Bit DOSen Sachen auch nur sehr sehr selten .
Jedenfalls hat es einiges an Hirnschmalz gekostet, die Klammerfunktion so zu minimieren (vorher, mit der jeweils eigenen Suchroutinge für jede Klammer, brachte das Programm 176 Bytes auf die Waage).
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf!
Selig, wer nichts zu sagen hat und trotzdem schweigt.
Ich hab mal n Klasse (oder package) in perl geschrieben. Man kann stdin und stdout als Variablen übergeben und debug output ausgeben lassen.
package brainfuck
Code:
package Brainfuck;
use strict;
sub new {
my $class = shift;
my %opts = @_;
my $self = {
stdin => exists $opts{stdin} ? $opts{stdin} : *STDIN,
stdout => exists $opts{stdout} ? $opts{stdout} : *STDOUT,
timeout => exists $opts{timeout} ? $opts{timeout} : 10,
ptr => [],
idx => 0,
};
bless $self, $class;
return $self;
}
sub eval {
my ($self, $expr) = @_;
eval {
local $SIG{ALRM} = sub { die; };
alarm $self->{timeout};
$self->_eval($expr);
};
alarm 0;
return;
}
sub reset {
my ($self) = @_;
$self->{ptr} = [];
$self->{idx} = 0;
return;
}
sub _eval {
my ($self, $expr) = @_;
my @whileStack;
my $openCount = 0; # open bracket count.
for my $char ($expr =~ /([\[\]\-+.,<>])/g) {
SWITCH: {
if (@whileStack && $char ne ']' && $char ne '[') {
push @whileStack, $char;
last SWITCH;
}
if ($char eq '>') { $self->{idx}++; last SWITCH; }
if ($char eq '<') { $self->{idx}--; last SWITCH; }
if ($char eq '+') { $self->{ptr}[$self->{idx}]++; last SWITCH; }
if ($char eq '-') {
if ($self->{ptr}[$self->{idx}]) {
$self->{ptr}[$self->{idx}]--;
last SWITCH;
}
}
if ($char eq ',') {
if (ref(\$self->{stdin}) eq 'GLOB') {
$self->{ptr}[$self->{idx}] = ord(getc $self->{stdin});
}
elsif (ref($self->{stdin}) eq 'SCALAR') {
$char = substr ${$self->{stdin}}, 0, 1, '';
# get the first char out of the stdin variable.
$self->{ptr}[$self->{idx}] = ord $char;
}
else {
my $randchar
= int(rand(95)) + 33; # we only want printable chars.
$self->{ptr}[$self->{idx}] = $randchar;
}
last SWITCH;
}
if ($char eq '.' && $self->{stdout}) {
if (ref(\$self->{stdout}) eq 'GLOB') {
print {$self->{stdout}} chr $self->{ptr}[$self->{idx}];
}
elsif (ref($self->{stdout}) eq 'SCALAR') {
${$self->{stdout}} .= chr $self->{ptr}[$self->{idx}];
}
last SWITCH;
}
if ($char eq '[') {
push @whileStack, $char;
$openCount++;
last SWITCH;
}
if ($char eq ']' && $openCount > 1) {
push @whileStack, $char;
$openCount--;
last SWITCH;
}
if ( $char eq ']'
&& @whileStack
&& $openCount == 1)
{
shift @whileStack; # get rid of the first opening bracket.
my $code = '';
while (my $char = shift @whileStack) {
$code .= $char;
}
while ($self->{ptr}[$self->{idx}]) {
$self->_eval($code);
}
$openCount = 0;
last SWITCH;
}
}
}
return;
}
1;
Einfaches Beispiel: (gibt 'Eydeet' aus)
Code:
use Brainfuck;
my $bf = Brainfuck->new();
$bf->eval('++++++++++[->+++++++>++++++++++++>++++++++++<<<]>-.>+.>.+..<-----.');
Oder mit Variablen: (gibt 'Hello there!' aus)
Code:
use Brainfuck;
my $instream = 'Hello there!';
my $out;
my $bf = Brainfuck->new(stdin => \$instream, stdout => \$out);
my $src = ',[.,]';
$bf->eval($src);
print $out;
edit: debug output is no more. (War eh recht uninformativ.)
edit: ich hab mal einen timeout eingebaut, damit code wie '+[+]' nicht einfach ewig läuft.
Code:
use Brainfuck;
my $bf = Brainfuck->new(timeout => 1); # one second
$bf->eval('+[+]');
print "foo";
Gibt "foo" nach einer Sekunde aus. (Zumindest auf einem unixoiden OS. Für windows keinerlei Garantie, da scheints ab und an mal Probleme mit alarm zu geben.)
Außerdem spoiler-tags zum Posting hinzugefügt. Tüdelü.
edit:
Hier mal ein kleines "interactive brainfuck" script: (package Brainfuck mal einfach hintendrangehängt, damit das "standalone" ist)
ibf.pl
Code:
#!/usr/bin/env perl
use warnings;
use strict;
use Term::ReadLine;
print "Interactive brainfuck. Commands that take longer than\n";
print "3 seconds will time out. h for help, q for quit.\n";
my $out = '';
my $bf = Brainfuck->new(stdout => \$out, stdin => 0, timeout => 3);
my $term = Term::ReadLine->new('brainfucker');
my $prompt = '{bf::in } ';
my $verbose = 0;
my @stored;
my @genericStored;
while (defined(my $line = $term->readline($prompt))) {
$bf->eval($line);
if ($out) {
print "{bf::out } $out\n";
$out = '';
}
$line =~ /^(?:q(?:uit)?|e(?:xit)?)/ and exit;
$line =~ /^p(?:rint)?$/ and dumpInfo($bf);
$line =~ /^s(?:tore)?$/ and store($bf, \@genericStored);
$line =~ /^s(?:tore)?\s+(\d+)$/ and store($bf, \@stored, $1);
$line =~ /^r(?:estore)?$/ and restore($bf, \@genericStored);
$line =~ /^r(?:estore)?\s+(\d+)$/ and restore($bf, \@stored, $1);
$line =~ /^c(?:lear)?$/ and $bf->reset();
$line =~ /^h(?:elp)?$/ and help();
$line =~ /\S/ and $term->addhistory($line);
}
sub restore {
my ($bf, $stored, $n) = @_;
if (defined $n) {
($bf->{idx}, $bf->{ptr})
= ($stored->[$n][0], [@{ $stored->[$n][1] }]);
}
else {
($bf->{idx}, $bf->{ptr})
= ($stored->[0], [@{ $stored->[1] }]);
}
return;
}
sub store {
my ($bf, $stored, $n) = @_;
if (defined $n) {
$stored->[$n] = [$bf->{idx}, [@{ $bf->{ptr} }]];
}
else {
@$stored = ($bf->{idx}, [@{ $bf->{ptr} }]);
}
return;
}
sub dumpInfo {
my ($bf) = @_;
my $ptr = '[';
my $i = 0;
my @tmpPtr = map { $_ || 0 } @{$bf->{ptr}};
for (0 .. ($bf->{idx} - @tmpPtr)) {
push @tmpPtr, 0;
}
for my $n (@tmpPtr) {
$ptr .=
($i == $bf->{idx})
? sprintf "(%d), ", $n
: sprintf "%d, ", $n;
$i++;
}
$ptr =~ s/, $/]/;
print "{bf::ptr }$ptr\n";
return;
}
sub help {
print "(cmd)------->(meaning)\n\n";
print "(clear)----->(reset ptr)\n";;
print "(store)----->(store ptr)\n";
print "(restore)--->(restore ptr)\n";
print "(store n)--->(store as ptr n)\n";
print "(restore n)->(restore ptr n)\n";
print "(print)----->(dump ptr)\n";
print "(help)------>(show help)\n";
print "(quit)------>(exit)\n";
print "(exit)------>(exit)\n\n";
return;
}
package Brainfuck;
use strict;
sub new {
my $class = shift;
my %opts = @_;
my $self = {
stdin => exists $opts{stdin} ? $opts{stdin} : *STDIN,
stdout => exists $opts{stdout} ? $opts{stdout} : *STDOUT,
timeout => exists $opts{timeout} ? $opts{timeout} : 10,
ptr => [],
idx => 0,
};
bless $self, $class;
return $self;
}
sub eval {
my ($self, $expr) = @_;
eval {
local $SIG{ALRM} = sub { die; };
alarm $self->{timeout};
$self->_eval($expr);
};
alarm 0;
return;
}
sub reset {
my ($self) = @_;
$self->{ptr} = [];
$self->{idx} = 0;
return;
}
sub _eval {
my ($self, $expr) = @_;
my @whileStack;
my $openCount = 0; # open bracket count.
for my $char ($expr =~ /([\[\]\-+.,<>])/g) {
SWITCH: {
if (@whileStack && $char ne ']' && $char ne '[') {
push @whileStack, $char;
last SWITCH;
}
if ($char eq '>') { $self->{idx}++; last SWITCH; }
if ($char eq '<') { $self->{idx}--; last SWITCH; }
if ($char eq '+') { $self->{ptr}[$self->{idx}]++; last SWITCH; }
if ($char eq '-') {
if ($self->{ptr}[$self->{idx}]) {
$self->{ptr}[$self->{idx}]--;
last SWITCH;
}
}
if ($char eq ',') {
if (ref(\$self->{stdin}) eq 'GLOB') {
$self->{ptr}[$self->{idx}] = ord(getc $self->{stdin});
}
elsif (ref($self->{stdin}) eq 'SCALAR') {
$char = substr ${$self->{stdin}}, 0, 1, '';
# get the first char out of the stdin variable.
$self->{ptr}[$self->{idx}] = ord $char;
}
else {
my $randchar
= int(rand(95)) + 33; # we only want printable chars.
$self->{ptr}[$self->{idx}] = $randchar;
}
last SWITCH;
}
if ($char eq '.' && $self->{stdout}) {
if (ref(\$self->{stdout}) eq 'GLOB') {
print {$self->{stdout}} chr $self->{ptr}[$self->{idx}];
}
elsif (ref($self->{stdout}) eq 'SCALAR') {
${$self->{stdout}} .= chr $self->{ptr}[$self->{idx}];
}
last SWITCH;
}
if ($char eq '[') {
push @whileStack, $char;
$openCount++;
last SWITCH;
}
if ($char eq ']' && $openCount > 1) {
push @whileStack, $char;
$openCount--;
last SWITCH;
}
if ( $char eq ']'
&& @whileStack
&& $openCount == 1)
{
shift @whileStack; # get rid of the first opening bracket.
my $code = '';
while (my $char = shift @whileStack) {
$code .= $char;
}
while ($self->{ptr}[$self->{idx}]) {
$self->_eval($code);
}
$openCount = 0;
last SWITCH;
}
}
}
return;
}
1;
Nein, du hast eine Version in C++ geschrieben, mit C hat das nichts zu tun. Btw, deine Version funktioniert nicht mit diesem bf-code (99 bottles of beer):
How many bottles of beer on teh wall?
Code:
99 Bottles of Beer in Urban Mueller's BrainF*** (The actual
name is impolite)
by Ben Olmstead
ANSI C interpreter available on the internet; due to
constraints in comments the address below needs to have the
stuff in parenthesis replaced with the appropriate symbol:
http://www(dot)cats(dash)eye(dot)com/cet/soft/lang/bf/
Believe it or not this language is indeed Turing complete!
Combines the speed of BASIC with the ease of INTERCAL and
the readability of an IOCCC entry!
>+++++++++[<+++++++++++>-]<[>[-]>[-]<<[>+>+<<-]>>[<<+>>-]>>>
[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<
-]<<-<-]+++++++++>[<->-]>>+>[<[-]<<+>>>-]>[-]+<<[>+>-<<-]<<<
[>>+>+<<<-]>>>[<<<+>>>-]>[<+>-]<<-[>[-]<[-]]>>+<[>[-]<-]<+++
+++++[<++++++<++++++>>-]>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-
]<<<<<<.>>[-]>[-]++++[<++++++++>-]<.>++++[<++++++++>-]<++.>+
++++[<+++++++++>-]<.><+++++..--------.-------.>>[>>+>+<<<-]>
>>[<<<+>>>-]<[<<<<++++++++++++++.>>>>-]<<<<[-]>++++[<+++++++
+>-]<.>+++++++++[<+++++++++>-]<--.---------.>+++++++[<------
---->-]<.>++++++[<+++++++++++>-]<.+++..+++++++++++++.>++++++
++[<---------->-]<--.>+++++++++[<+++++++++>-]<--.-.>++++++++
[<---------->-]<++.>++++++++[<++++++++++>-]<++++.-----------
-.---.>+++++++[<---------->-]<+.>++++++++[<+++++++++++>-]<-.
>++[<----------->-]<.+++++++++++..>+++++++++[<---------->-]<
-----.---.>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>>+++
+[<++++++>-]<--.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<.
><+++++..--------.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++
++++++++++++.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<++
+++++++>-]<--.---------.>+++++++[<---------->-]<.>++++++[<++
+++++++++>-]<.+++..+++++++++++++.>++++++++++[<---------->-]<
-.---.>+++++++[<++++++++++>-]<++++.+++++++++++++.++++++++++.
------.>+++++++[<---------->-]<+.>++++++++[<++++++++++>-]<-.
-.---------.>+++++++[<---------->-]<+.>+++++++[<++++++++++>-
]<--.+++++++++++.++++++++.---------.>++++++++[<---------->-]
<++.>+++++[<+++++++++++++>-]<.+++++++++++++.----------.>++++
+++[<---------->-]<++.>++++++++[<++++++++++>-]<.>+++[<----->
-]<.>+++[<++++++>-]<..>+++++++++[<--------->-]<--.>+++++++[<
++++++++++>-]<+++.+++++++++++.>++++++++[<----------->-]<++++
.>+++++[<+++++++++++++>-]<.>+++[<++++++>-]<-.---.++++++.----
---.----------.>++++++++[<----------->-]<+.---.[-]<<<->[-]>[
-]<<[>+>+<<-]>>[<<+>>-]>>>[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]
>[<+>-]>[<<++++++++++>>>+<-]<<-<-]+++++++++>[<->-]>>+>[<[-]<
<+>>>-]>[-]+<<[>+>-<<-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<>>[<+>-]<
<-[>[-]<[-]]>>+<[>[-]<-]<++++++++[<++++++<++++++>>-]>>>[>+>+
<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>[-]>[-]++++[<++++++++>
-]<.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<.><+++++..---
-----.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++++++++++++++
.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<+++++++++>-]<-
-.---------.>+++++++[<---------->-]<.>++++++[<+++++++++++>-]
<.+++..+++++++++++++.>++++++++[<---------->-]<--.>+++++++++[
<+++++++++>-]<--.-.>++++++++[<---------->-]<++.>++++++++[<++
++++++++>-]<++++.------------.---.>+++++++[<---------->-]<+.
>++++++++[<+++++++++++>-]<-.>++[<----------->-]<.+++++++++++
..>+++++++++[<---------->-]<-----.---.+++.---.[-]<<<]
@
Der bleibt bei 91 (bottles of beer) hängen. Das Lustige daran ist: mein bf-Interpreter in perl bleibt genau an der Selben Stelle hängen. Also haben wir wohl beide denselben Denkfehler irgendwo, der dann in diesem Bug resultiert.
Hier mal eine Version in C: (Die läuft komplett durch bei den Bottles of Beer und auch bei sonst allem getesteten)
brainfucker.c
Code:
#include <stdio.h>
#include <stdlib.h>
typedef struct bfptrStruct {
char *ptr;
int pos;
int size;
} *bfptr;
int fetchSrc(char **src, const char *filename);
bfptr getPtr(int size);
int fileLengthSeekHack(const char *filename);
int mystrlen(char *str);
void myrealloc(char *ptr, int offset, int size);
void eval(bfptr bf, char *src, int srclen);
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <brainfuck-srcfile>\n", argv[0]);
exit(EXIT_FAILURE);
}
char *src;
int len = fetchSrc(&src, argv[1]); /* fills src and returns strlen(src) */
bfptr bf = getPtr(64);
eval(bf, src, len);
return EXIT_SUCCESS;
}
bfptr getPtr(int size) {
bfptr bf = malloc(sizeof(struct bfptrStruct));
bf->ptr = calloc(size, sizeof(char));
/* calloc() ( other than malloc() ) initializes the memory to zeros */
if (bf->ptr == NULL) {
fprintf(stderr, "Couldn't allocate memory for ptr\n");
exit(EXIT_FAILURE);
}
bf->size = size;
bf->pos = 0;
return bf;
}
int fetchSrc(char **src, const char *filename) {
int filesize = fileLengthSeekHack(filename);
FILE *fd;
fd = fopen(filename, "r");
if (fd == NULL) {
fprintf(stderr, "Couldn't open %s for reading.\n", filename);
exit(EXIT_FAILURE);
}
*src = malloc(filesize); /* now that should be enough in every case (i guess) */
int i = 0;
char c;
while ((c = fgetc(fd)) != EOF) {
if (
c == '+'
|| c == '-'
|| c == '<'
|| c == '>'
|| c == '['
|| c == ']'
|| c == '.'
|| c == ','
) {
(*src)[i] = c;
i++;
}
}
(*src)[i] = '\0';
return i;
}
void eval(bfptr bf, char *src, int srclen) {
int i;
for (i = 0; src[i]; i++) {
switch(src[i]) {
case '.': {
fputc(bf->ptr[bf->pos], stdout);
break;
}
case ',': {
bf->ptr[bf->pos] = fgetc(stdin);
break;
}
case '+': {
bf->ptr[bf->pos]++;
break;
}
case '-': {
bf->ptr[bf->pos]--;
break;
}
case '<': {
if (bf->pos == 0) {
break;
}
bf->pos--;
break;
}
case '>': {
if (bf->pos == (bf->size - 1)) {
myrealloc(bf->ptr, bf->size, bf->size * 2);
bf->size *= 2;
}
bf->pos++;
break;
}
case '[': {
char *whileStack = malloc(srclen - i);
i++; /* now src[i] is one after the '[' */
int j = 0;
int openc = 1;
for (; openc > 0; i++, j++) {
if (src[i] == '[') {
openc++;
}
if (src[i] == ']') {
openc--;
if (openc == 0) {
break; /* break out of the for-loop */
}
}
whileStack[j] = src[i];
}
whileStack[j] = '\0';
int whileLen = mystrlen(whileStack);
while (bf->ptr[bf->pos]) {
eval(bf, whileStack, whileLen);
}
break;
}
}
}
return;
}
int mystrlen(char *str) {
int i = 0;
while (str[i]) {
i++;
}
i++;
return i;
}
void myrealloc(char *ptr, int offset, int size) {
if (realloc(ptr, size) == NULL) {
fprintf(stderr, "Couldn't allocate memory for ptr\n");
exit(EXIT_FAILURE);
}
int i;
for (i = offset; i < size; i++) {
ptr[i] = 0;
}
return;
}
int fileLengthSeekHack(const char *filename) {
int endpos;
FILE *fd = fopen(filename, "rb");
if (fd == NULL) {
fprintf(stderr, "Couldn't open %s for read access\n", filename);
exit(EXIT_FAILURE);
}
fseek(fd, 0, SEEK_END);
endpos = ftell(fd);
fclose(fd);
return endpos;
}
Nein, du hast eine Version in C++ geschrieben, mit C hat das nichts zu tun.
Jap, richtig. Ich habe aber auch nicht gesagt, dass es nur C ist. Da mein Code auch nicht sauber in C++ ist(der ist schon lang genug), schreibe ich immer diese Art von Quelltext als C/C++.
Zitat:
Der bleibt bei 91 (bottles of beer) hängen.
Danke für den Hinweis. Das Problem ist jetzt gepatched. Lag an fehlende Überprüfung für Underflows.
Hab mein Brainfuck.pm auch mal geupdated. Aber soweit ich weiß tritt bei "-" auf einer Null-Zelle eh undefiniertes Verhalten auf. Einfach weil brainfuck nicht standardisiert ist. Naja.
Btw, jetzt macht dein Programm ein coredump. Wenn man diese Zeilen hier löscht:
Code:
if(source)
{
delete[] source;
source = NULL;
}
gehts problemlos. (Ist eh etwas redundant kurz vor Programmende nochmal Sachen zu free()en.)
Zitat:
Ich habe aber auch nicht gesagt, dass es nur C ist.
Stimmt, es ist gar kein C.
Zitat:
Da mein Code auch nicht sauber in C++ ist(der ist schon lang genug), schreibe ich immer diese Art von Quelltext als C/C++.
Oha, unsauberes C++ ist C? ...
Du benutzt ausschließlich C++ header, du benutzt Objekte (str, ifstream, ...), du benutzt "bool" und namespaces. Das sind alles reine C++ features. Selbst wenn du ein C Programm geschrieben hättest, welches auch auf einem C++ compiler kompilieren würde (was nicht immer gilt), wäre es sinnfrei es "C/C++" zu nennen. Dann würd man einfach C sagen. Egal, genug offtopic.
Richtig, es ist nicht C. Dann sind paar Sachen in C++ unerwünscht, dazu gehören z.b. defines, ich verwende auch nicht nur strings sondern auch C-Strings.
Natürlich ist es nicht C, und wird von keinem C Compiler compilliert. Aber da C eh eine Untermenge von C++ ist, ist es egal ob ich C++ oder C/C++ schreibe. C/C++ beudetet nicht, C oder C++ sonder C mit(Vereinigung) C++ vermischt.
Ich wollte nur verdeutlichen, dass ich nicht nach Stroustrup vorgehe.
Aber genug zum OT.
Und danke für deine konstruktive Beiträge zum Code.
Mal was neues: Brainfuck-interpreter in Haskell !! Kann alles bis auf einlesen von stdin; also alle BF-Zeichen bis auf ','.
CODE
Code:
import Char
parse :: String -> String
parse s = fst (prs s [0] 0)
where prs :: String -> [Int] -> Int -> (String,([Int],Int))
prs [] a i = ([],(a,i))
prs ('>':xs) a i = prs xs (a++[0]) (i+1)
prs ('<':xs) a i | i > 0 = prs xs a (i-1)
| otherwise = error "index gets negative!"
prs ('+':xs) a i = prs xs (inc a i) i
prs ('-':xs) a i = prs xs (dec a i) i
prs ('.':xs) a i = ((echoChr a i):(fst parsed),snd parsed)
where parsed = prs xs a i
prs ('[':xs) a i = ((whileout ++ outnew),snd parsed)
where wparsed = while xs a i
whileout = fst wparsed
whilea = (fst.snd) wparsed
whilei = (snd.snd) wparsed
parsed = prs (skipwhile xs) whilea whilei
outnew = fst parsed
prs (']':xs) a i = ([],(a,i))
prs (_:xs) a i = prs xs a i
index :: [Int] -> Int -> Int
index (x:xs) 0 = x
index (x:xs) i | i > 0 = index xs (i-1)
| otherwise = error "negative index!"
inc :: [Int] -> Int -> [Int]
inc (x:xs) 0 = (x+1):xs
inc (x:xs) i | i > 0 = x:(inc xs (i-1))
| otherwise = error "negative index!"
dec :: [Int] -> Int -> [Int]
dec (x:xs) 0 = (x-1):xs
dec (x:xs) i | i > 0 = x:(dec xs (i-1))
| otherwise = error "negative index!"
echoChr :: [Int] -> Int -> Char
echoChr (x:xs) 0 | x >= 0 = chr x
| otherwise = error "Can't display negative number!"
echoChr (x:xs) i | i > 0 = echoChr xs (i-1)
| otherwise = error "negative index!"
while :: String -> [Int] -> Int -> (String,([Int],Int))
while xs a i | index a i == 0 = ([],(a,i))
| otherwise = ((outnew ++ (fst wparsed)),(whilea,whilei))
where wparsed = while xs anew inew
whilea = (fst.snd) wparsed
whilei = (snd.snd) wparsed
parsed = prs xs a i
outnew = fst parsed
anew = (fst.snd) parsed
inew = (snd.snd) parsed
skipwhile :: String -> String
skipwhile (']':xs) = xs
skipwhile (_:xs) = skipwhile xs
helloWorld :: String
helloWorld = "++++++++[>+++++++++<-]>.<+++++[>++++++<-]>-.+++++++..+++.<++++++++[>>++++<<-]>>.<<++++[>------<-]>.<++++[>++++++<-]>.+++.------.--------.>+."
Die bottles-of-beer-Version, die den Fehler auslöste, halte ich für relativ schlecht, dass sie Überläufe und eine Wortbreite von genau 8 Bit benötigt, um zu funktionieren. Diese hier dürfte auch bei anderen Implementierungen funktionieren: http://99-bottles-of-beer.net/langua...nfuck-101.html
Hier noch eine Brainfuck-Implementierung, mit nach links und rechts unbegrenzt Speicher:
Code:
import Control.Arrow
import Data.Char
import Data.Word
import qualified Data.Map as M
type Val = Word8 -- size of one field, change to Integer for arbitrary sized Integers
type BFState = (Integer,M.Map Integer Val)
readVal = maybe 0 id . uncurry M.lookup
changeVal f (ptr,mem) = (ptr,M.alter (Just . maybe (f 0) f) ptr mem)
interpretBF :: Integer -> String -> BFState -> IO (BFState,String)
interpretBF _ [] x = return (x,[])
interpretBF depth (c:cs) bf
| c `elem` "+-><" = interpretBF depth cs (simple c bf)
| c == '.' = putChar (chr . fromIntegral $ readVal bf) >> interpretBF depth cs bf
| c == ',' = getLine >>= interpretBF depth cs . flip changeVal bf . const . read
| c == '[' = if readVal bf /= 0
then runBlock cs bf >>= uncurry (flip $ interpretBF depth)
else interpretBF depth cs bf >>= flip (interpretBF depth) bf . snd
| c == ']' = case depth of
0 -> error "mismatched brackets"
1 -> return (bf,cs)
_ -> interpretBF (depth - 1) cs bf
| otherwise = interpretBF depth cs bf
where simple '+' = changeVal (+1)
simple '-' = changeVal $ subtract 1
simple '>' = first (+1)
simple '<' = first $ subtract 1
runBlock cs st = interpretBF 1 cs st >>= \(st',rest) ->
if readVal st' /= 0 then runBlock cs st' else return (st',rest)
interpret code = interpretBF 0 code (0,M.empty)