Brainfuck Interpreter

CDW

0
Mitarbeiter
Eingereicht von Scorn07:
Programmieraufgabe: Brainfuck Interpreter

Ich gehe mal davon aus, dass hier die meisten Brainfuck kennen. Für alle anderen: http://de.wikipedia.org/wiki/Brainfuck

Die Aufgabe ist es für Brainfuck einen Interpreter zu schreiben.
Von meiner Seite gibts es noch: achtet auf die Schleifenkonstrukte - diese können verschachtelt werden ;)
 
Hi,

Hier mal meine Lösung:

Code:
import sys

CS = ""
DS = dict()
DP = 0
IP = 0
JS = list()

if sys.argv[1] == "-c":
	CS = sys.argv[2]
else:
	CS = str().join(open(sys.argv[1],'r').readlines())

while IP <= len(CS) - 1:
	if CS[IP] == "<":
		DP = DP - 1
        elif CS[IP] == ">":
		DP = DP + 1
	elif CS[IP] == "-":
		if not DS.has_key(DP):
			DS[DP] = 0
		DS[DP] = (DS[DP] - 1)  % 256
	elif CS[IP] == "+":
		if not DS.has_key(DP):
			DS[DP] = 0
		DS[DP] = (DS[DP] + 1) % 256
	elif CS[IP] == ".":
		if not DS.has_key(DP):
			DS[DP] = 0
		sys.stdout.write(chr(DS[DP]))
	elif CS[IP] == ",":
		DS[DP] = ord(sys.stdin.read(1))
	elif CS[IP] == '[':
		if not DS.has_key(DP):
			DS[DP] = 0
		if DS[DP] != 0:
			JS.append(IP)
		else:
			Token = 1
			IP = IP + 1
			while Token > 0:
				if CS[IP] == '[':
					Token = Token + 1
				elif CS[IP] == ']':
					Token = Token - 1
				IP = IP + 1
			IP = IP - 1
	elif CS[IP] == "]":
		IP = JS.pop() - 1
	IP = IP + 1
Gruß Chris
 
Code:
#!/usr/bin/env perl
use strict;

BEGIN {undef $/}

my $bf_src = <>;

my %transHash = (
                    ">" => "++ptr;",
                    "<" => "--ptr;",
                    "+" => "++*ptr;",
                    "-" => "--*ptr;",
                    "." => "putchar(*ptr);",
                    "," => "*ptr = getchar();",
                    "[" => "while (*ptr) {",
                    "]" => "}"
                );

print "#include <stdio.h>\n";
print "#include <stdlib.h>\n";
print "int main(void) {\n";

my $incr_c = () = $bf_src =~ />/g;
$incr_c++;
print "int *ptr = malloc($incr_c * sizeof(int));\n";

foreach (split(//, $bf_src)) {
    print "$transHash{$_} ";
}

print "return 0;}\n";
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.
 
Das ist jetzt wahrscheinlich sehr umständlich, aber hier ist meine Lösung in C++:
Code:
#include <iostream>
#include <fstream>
#define FIELD_SIZE 1024
using namespace std;

int findClosingBracket(string &bfCode, int pos)
{
    unsigned curDepth = 0;
    for (unsigned i = 1; i < bfCode.length() - pos; i++) {
        switch (bfCode[pos + i]) {
            case '[':
                ++curDepth;
                break;
            case ']':
                if (curDepth == 0) return i;
                --curDepth;
                break;
        }
    }
    return -1;
}

void bfParser(char *field, int fieldPos, string &bfCode)
{
    int posClosingBracket;

    for (unsigned codePos = 0; codePos < bfCode.length(); codePos++) {
        switch (bfCode[codePos]) {
            case '+':
                ++field[fieldPos];
                break;
            case '-':
                --field[fieldPos];
                break;
            case '>':
                ++fieldPos;
                break;
            case '<':
                --fieldPos;
                break;
            case '.':
                cout << field[fieldPos];
                break;
            case ',':
                cin >> field[fieldPos];
                break;
            case '[':
                posClosingBracket = findClosingBracket(bfCode, codePos);
                if (field[fieldPos] > 0) {
                    string newCode = bfCode.substr(codePos+1, posClosingBracket - 1);
                    bfParser(field, fieldPos, newCode);
                    --codePos; // hier bleiben!
                } else {
                    codePos += posClosingBracket;
                }
                break;
            case ']':
                break;
        }
        if (fieldPos < 0 || fieldPos >= FIELD_SIZE) {
            cerr << "Index out of Range: " << fieldPos << endl;
            return;
        }
    }
}
    

int main(int argc, char **argv)
{
    if (argc != 2) {
        cerr << "Usage: %s <file>\n" << argv[0] << endl;
        return 1;
    }
    
    char *field = new char[FIELD_SIZE];
    
    ifstream codeFile(argv[1]);
    if (!codeFile.is_open()) {
        cerr << "Konnte Datei nicht oeffnen!" << endl;
        return 1;
    }

    string bfCode;
    while (!codeFile.eof()) {
        string line;
        getline(codeFile, line);
        bfCode += line;
    }
    
    bfParser(field, 0, bfCode);
}
Was der Interpreter nicht kann:
* unendlich großes Feld
* unendlich große Zellen

Ich bin jetzt aber zu faul, das irgendwie nachzubessern ;)

Mfg, Eydeet.
 
siehe meine signatur ;)

EDIT: da ich jetzt ne andere Signatur hab ;) :
Code:
char m[9999],*n[99],*r=m,*p=m+5000,**s=n,d,c;main(){for(read(0,r,4000);c=*r;
r++)c-']'||(d>1||(r=*p?*s:(--s,r)),!d||d--),c-'['||d++||(*++s=r),d||(*p+=c==
'+',*p-=c=='-',p+=c=='>',p-=c=='<',c-'.'||write(2,p,1),c-','||read(2,p,1));}
 
Brainfuck Interpreter in 132 Bytes :p
Code:
.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).
 
Ich hab mal n Klasse (oder package) in perl geschrieben. Man kann stdin und stdout als Variablen übergeben und debug output ausgeben lassen.
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)
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;
 
Hier meine Version (und mein erster Beitrag) :D:
Code:
#!/usr/bin/perl

use strict;
use warnings;

my $bf = "++++++++++
[
   >+++++++>++++++++++>+++>+<<<<-
]                       // Schleife zur Vorbereitung der Textausgabe
>++.                    // Ausgabe von 'H'
>+.                     // Ausgabe von 'e'
+++++++.                // 'l'
.                       // 'l'
+++.                    // 'o'
>++.                    // Leerzeichen
<<+++++++++++++++.      // 'W'
>.                      // 'o'
+++.                    // 'r'
------.                 // 'l'
--------.               // 'd'
>+.                     // '!'
>.                      // Zeilenumbruch";
my @bfArr = split(//,$bf);

my @register = ();
my $regPointer = 0;

for (my $bfIndex = 0; $bfIndex < @bfArr; $bfIndex++) {
  if ($bfArr[$bfIndex] eq '-') {
    $register[$regPointer]--;
  } elsif ($bfArr[$bfIndex] eq '+') {
    $register[$regPointer]++;
  } elsif ($bfArr[$bfIndex] eq '>') {
    $regPointer++;
  } elsif ($bfArr[$bfIndex] eq '<') {
    $regPointer--;
  } elsif ($bfArr[$bfIndex] eq '.') {
    print chr ($register[$regPointer]);
  } elsif ($bfArr[$bfIndex] eq ',') {
    $register[$regPointer] = ord (chomp ($register[$regPointer] = <STDIN>));
  } elsif ($bfArr[$bfIndex] eq '[') {
    if ($register[$regPointer] == 0) {
      $bfIndex++ while ($bfArr[$bfIndex] ne ']');
      $bfIndex--;
    }
  } elsif ($bfArr[$bfIndex] eq ']') {
     if ($register[$regPointer] != 0) {
       $bfIndex-- while ($bfArr[$bfIndex] ne '[');
       $bfIndex--;
     }
  }
}

Nichts besonderes, ziemlich einfach gehalten :)
 
Ich habe jetzt auch eine Version in C/C++ geschrieben.
Getestet wurde mit "Hello World" aus Wiki.

Code:
#include <iostream>
#include <fstream>
#include <string>

//some datatype definition
//todo: make it dynamic for "infinit" size -> array
#define CellType unsigned int
unsigned int CellSize=30000;

//Define brainfuck operators
enum
{
    OP_NEXT=0,
    OP_PREV,
    OP_PLUS,
    OP_MINUS,
    OP_OUT,
    OP_INP,
    OP_JUMP_IN,
    OP_JUMP_OUT
};

char operators[] = {'>','<',
                    '+','-',
                    '.',',',
                    '[',']'};
//is the character an operator?
bool isOP(char op);
//extract sourcecode from a file
bool extractSource(char *filename, char *source);
//interpret brainfuck sourcecode
bool interpret(std::string source, CellType *cells);
//extract loop out of source.
//ip = instruction pointer = current position in code
//posBracket = returns the value of close loop bracket
//return value: string with loopsource
//                "-1" if error
std::string extractLoop(std::string source, int ip, int &posBracket);


int main(int argc, char *argv[])
{
    //test wether the filename is given
    if(argc<2)
    {
        std::cout<<"usage: brainfuck filename\n";
        return -1;
    }

    //contains sourcecode
    char *source;
    //get the source from the filename
    extractSource(argv[1], source);

    //free the buffer
    if(source)
    {
        delete[] source;
        source = NULL;
    }

    return 0;
}

//is operator?
bool isOP(char op)
{
    //go through all operators
    for(int i=0; i<sizeof(operators)/sizeof(char); i++)
    {
        if(op==operators[i])
            return true;
    }

    return false;
}

//extract sourcecode from filename
bool extractSource(char *filename, char *source)
{
    //get the filesize
    std::ifstream input(filename);
    if(!input)
        return false;

    input.seekg(0, std::ios::end);
    unsigned int fileSize = input.tellg();

    //create source buffer
    source = new char[fileSize+1];

    //reset file stream to the beginning of the file
    input.clear();
    input.seekg(0, std::ios::beg);

    if(input.is_open())
    {
        //get the source, cancel all comments and other characters
        int index=0;
        while(!input.eof() && index<=fileSize)
        {
           input.get(source[index]);
           if(isOP(source[index]))
            index++;
        }
        source[index]='\0';

        input.close();
    }

    //create our cells
    CellType *cell;
    cell = new CellType[CellSize];
    memset(cell, 0, CellSize);

    //interpret brainfuck
    interpret(source, cell);
    if(cell)
    {
        delete[] cell;
        cell = NULL;
    }

    return true;
}

//extract loop from sourcecode
std::string extractLoop(std::string source, int ip, int &posBracket)
{
    //save the position of closing bracket
    int posClose=-1;
    //size of interlaced loops
    int numOpen=1;
    //go through the code end extract the whole loop
    for(int index = ip+1; index<source.size(); ++index)
    {
        //found closing bracket
        if(source[index]==operators[OP_JUMP_OUT])
        {
            //decrease interlaced depth
            --numOpen;
            //save if we have the outermost loop
            if(numOpen==0)
            {
                posClose=index;
                break;
            }
        }
        //found begining of another loop
        else if(source[index]==operators[OP_JUMP_IN])
        {
            //increase the depth
            ++numOpen;
        }
    }

    if(numOpen)
    {
        std::cout<<"Error: Loop is not closed!"<<std::endl;
        return "-1";
    }

    //save our closing bracket position
    posBracket = posClose;

    //extract the loop and save it
    int codeSize = posClose-ip-1;
    std::string loop;
    return source.substr(ip+1, codeSize);
}//end extractLoop

//interpret brainfuck source
bool interpret(std::string source, CellType *cell)
{
    //position in the cell
    static CellType dataPointer = 0;

    //go through the loop and interpret
    for(int ip=0; ip<source.size(); ++ip)
    {
        //create more buffer, if not sufficient
       if(ip>=CellSize)
       {
           CellSize *= 2;
           realloc(cell, CellSize);
       }

       if(source[ip]==operators[OP_NEXT])
       {
            ++dataPointer;
       }
       else if(source[ip]==operators[OP_PREV])
       {
            --dataPointer;
       }
       else if(source[ip]==operators[OP_PLUS])
       {
            ++cell[dataPointer];
       }
       else if(source[ip]==operators[OP_MINUS])
       {
           if(cell[dataPointer])
                --cell[dataPointer];

       }
       else if(source[ip]==operators[OP_OUT])
       {
            std::cout<<static_cast<char>(cell[dataPointer]);
       }
       else if(source[ip]==operators[OP_INP])
       {
            std::cin>>cell[dataPointer];
       }
       else if(source[ip]==operators[OP_JUMP_IN])
       {
            std::string loop;
            int posClose;
            loop = extractLoop(source, ip, posClose);

            if(loop == "-1")
                return false;

            while(cell[dataPointer])
            {
                interpret(loop, cell);
            }

            ip=posClose;
       }
    }

    return true;
}//end interpret

Binary für Windows und Linux mit Source kann man auch hier downloaden:
Simple Brainfuck Interpreter
 
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):
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)
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++.

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.
 
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.)

Ich habe aber auch nicht gesagt, dass es nur C ist.
Stimmt, es ist gar kein C.
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.
 
Oha, unsauberes C++ ist C?
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:
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/language-brainfuck-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)
 
Hallo,

ich habe mich in letzter Zeit damit beschäftigt, Brainfuck schnell und effizient auszuführen.
Es ist bekannt, dass kompiliertes Brainfuck (ob nach Assembler oder C) viel schneller ist, als interpretiertes Brainfuck - obgleich das interpretieren wahrscheinlich oft sicherer ist als das Ausführen eines Kompilats.
Compiler sind aber oft entweder plattformabhängig (wenn nach Assembler kompiliert oder direkt eine ausführbare Datei erstellt wird) oder schwer zu handhaben (wenn nach C oder eine andere Hochsprache kompiliert wird - der Nutzer muss den Code erst kompilieren) bzw. abhängig von externen Programmen.
Also habe ich versucht, die Vorteile zu vereinen indem ich einen Compiler für Brainfuck geschrieben habe, der das Ergebnis der Kompilierung auf dem Heap speichert und von dort aus ausführt. Er konvertiert den Brainfuckcode direkt in 80386 32bit Opcodes.
Daher ist der Compiler zwar auf den Intel 80386 beschränkt (außer man schreibt die opcode-Makros um) aber nicht auf ein bestimmtes Betriebssystem.

Vielleicht lasst ihr das ja noch als (halben) Interpreter gelten - ein richtiger Compiler ist es ja nicht :D

C Quelltext
C Quelltext + (mit gcc kompilierte) EXE + englische Readme

Das Programm kann übrigens dank Kompilierung und Brainfuck Code Optimierung mit Brainfuck Compilern mithalten.

mfg Christian
 
Zurück
Oben