Brainfuck Interpreter

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Ein fehler weg ein neuer dazu die ASCII ausgaben des Hello Worlds programm stimmen nicht.

Code: Alles auswählen

def bf(code):
    code=[s for s in code if s in ("<",">","+","-",".",",")]
    cellindex=0
    cells=[0]*30000
    codeindex=0
    stack=[]
    while codeindex < len(code):
        index=code[codeindex]
        if index==">":
            cellindex +=1
           
        elif index=="<":
            cellindex -=1
            
        elif index=="+":
            cells[cellindex] +=1
           
        elif index=="-":
            cells[cellindex] -=1
            
        elif index==".":
            print(chr(cells[cellindex]), end="")
         

        elif index==",":
            cells[cellindex] = ord(input())
        

        elif index=="[":
            if cells[cellindex]== 0:
                stack.append(codeindex)
                codeindex=code.index("]",codeindex)
                
        

        elif index=="]":
            codeindex=stack.pop()
            codeindex -=1
            
               
        codeindex +=1    
                
                
BlackJack

@fail: Die Behandlung von '[' ist falsch. Eventuell hast Du den Zweck von dem Stack nicht verstanden und wann der wofür benutzt werden muss. Mach Dir das mal an einem ganz einfachen Beispiel klar. Spiel das in Gedanken durch, sei Python-Interpreter und scheibe Dir für jeden Programmschritt die aktuellen Werte auf ein Blatt Papier.

Ausserdem ist die Suche der passenden schliessenden Klammer falsch. Zum einen der selbe Fehler den ich schon bei der rekursiven Variante ganz am Anfang mal angemerkt hatt: Es wird immer nur nach der *ersten* Klammer im gesamten `code` gesucht. Aber selbst wenn man das anpasst zu „suche die erste schliessende Klammer ab der aktuellen öffnenden Klammer”, ist das immer noch zu simpel gedacht, denn Du musst nicht die *erste*, sondern die *passende* Klammer suchen. Beide Fehler würden bei dem „Hallo Welt”-Programm noch nicht auffallen, aber auch nur weil es nicht mehrere und auch keine verschachtelten Klammern in dem Programm gibt.

Edit: Die Behandlung von ',' entspricht übrigens auch nicht der BF-Definition. Da soll nicht ein Zeichen plus Eingabetaste gelesen werden, sondern wirklich nur ein Bytewert von der Standardeingabe.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Okay jetzt hab ich Probleme erstens wie finde ich die passende Klammer?
und zweitens wie kann ich nur ein zeichen lesen lassen?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Hat zwar nichts mit fails Problemen zu tun, aber hier mal ein bf2py-Converter:

Code: Alles auswählen

cmds={
    '>':'i+=1',
    '<':'i-=1',
    '+':'c[i]+=1',
    '-':'c[i]-=1',
    '.':'sys.stdout.write(chr(c[i]))',
    ',':'c[i]=ord(sys.stdin.read(1))',
    '[':'while c[i]:',
}
brackets= {'[':1, ']':-1}
def bf(code):
    pcode=["c=[0]*100","i=0"]
    ind=0
    for c in code:
        if c in cmds:
            pcode.append(' '*ind+cmds[c])
        ind+=brackets.get(c,0)
    exec '\n'.join(pcode)
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Die erste öffnende Klammer schliesst sich mit der letzten schliessender Klammer.
Die zweite öffnende Klammer schliesst sich mit der zweitletzten schliessender Klammer.

etc.

Oder?
BlackJack

@fail: Nein, das würde nur funktionieren wenn *alle* Schleifen ineineinander geschachtelt sind, aber nicht wenn es Schleifen gibt, die nacheinander ausgeführt werden. Beispiel wo es nicht nach dem von Dir beschriebenen Muster funkioniert:

Code: Alles auswählen

...[...[...]...]...[...]... BF-Code
   1   2   3   4   5   6    Klammer-Nr.
012345678901234567890123456 Code Index
          1         2
Hier gehören zusammen: Klammern 1 und 4, 2 und 3, 5 und 6.

Du musst die Zeichen abgehen und dir Merken wie viele Klammern aktuell noch offen sind, für die Du noch keine schliessende Klammer gesehen hast.

Am besten macht man das am Anfang in einem Durchlauf und merkt sich in einem Wörterbuch welche Indizes in den Code zusammen gehören. Jeweils in beide Richtungen. Da kann man sich mit einem Stack die Positionen der noch offenen Klammern merken. Dann braucht man während des BF-Programmablaufs keinen Stack mehr und kann die jeweils andere Klammerposition ganz einfach ermitteln in dem man im Wörterbuch nachschaut. Für das Beispiel oben sähe das Wörterbuch so aus:

Code: Alles auswählen

d = {3: 15, 15: 3, 7: 11, 11: 7, 19: 23, 23: 19}
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Wie kann ich das mit dem Suchen machen?
BlackJack

@fail: Das habe ich im Grunde doch beschrieben. Ich nehme mal an *Du* kannst bei einem beliebigen Ausdruck mit korrekt verschachtelten Klammern heraus finden welche zusammen gehören. Dann musst Du ja auch beschreiben können wie Du auf das Ergebnis kommst. Das musst Du dann nur noch so genau/formal beschreiben, dass man es als Quelltext schreiben kann.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Wo ist jetzt der fehler das Hello world Programm gibt irgendeine Komische ausgabe aus

Code: Alles auswählen

def Klammernpaare(code):
    codeindex=0
    listeoffen=[]
    zuposition={}
    offenposition={}
    while codeindex<len(code):
        if code[codeindex]=="[":
            listeoffen.append(codeindex)
        elif code[codeindex]=="]":
            letze=listeoffen.pop()
            zuposition[letze]=codeindex
        codeindex +=1
    for i in zuposition:
        offenposition[zuposition[i]]= i
	
    return offenposition, zuposition


def bf(code):
    #Kommentare entfernen
    code=[s for s in code if s in ("<",">","+","-",".",",")]
    #aktuelle Zelle
    cellindex=0
    #Zellen
    cells=[0]*30000
    #Position im Code
    codeindex=0
    #Klammernpaare finden für Schleifen
    offenposition, zuposition= Klammernpaare(code)
    
    while codeindex < len(code):
        index=code[codeindex]
        if index==">":
            cellindex +=1
           
        elif index=="<":
            cellindex -=1
            
        elif index=="+":
            cells[cellindex] +=1
           
        elif index=="-":
            cells[cellindex] -=1
            
        elif index==".":
            print(chr(cells[cellindex]), end="")
         

        elif index==",":
            cells[cellindex] = ord(input())
        

        elif index=="[":
            if cells[cellindex]== 0:
                codeindex=zuposition[codeindex]
                codeindex -=1
                
        

        elif index=="]":
            codeindex=offenposition[codeindex]
            codeindex -=1
            
               
        codeindex +=1    
                
                
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@fail: Dafür ist das Testen der einzelnen Funktionen gut. Tut jedes BF-Commando das was es soll, werden die Klammern richtig ermittelt, springt [ und ] an die richtige Stelle, usw.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Ihr müsst mir helfen ich hab keine Ahnung wo der Fehler liegt. Wahrscheinlich etwas mit den Schleifenindex
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

1. vor die while-Schleife ein print(offenposition,zuposition)
2. in die while Schleife ein print(codeindex,index)
und dann von Hand Dein brain**** Programm durcharbeiten und schauen, ob der Computer auf die selben Ergebnisse kommt.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Scheint jetzt zu funktionieren. Noch eine Frage wie kann ich die Eingabe so machen das ich nicht enter drücken muss?

Code: Alles auswählen

def Klammernpaare(code):
        codeindex=0
        listeoffen=[]
        zuposition={}
        offenposition={}
        while codeindex<len(code):
            if code[codeindex]=="[":
                listeoffen.append(codeindex)
            elif code[codeindex]=="]":
                letze=listeoffen.pop()
                zuposition[letze]=codeindex
            codeindex +=1
        for i in zuposition:
            offenposition[zuposition[i]]= i
	
        return offenposition, zuposition


def bf(code):
    #Kommentare entfernen
    code=[s for s in code if s in ("<",">","+","-",".",",","[","]")]
    #aktuelle Zelle
    cellindex=0
    #Zellen
    cells=[0]*30000
    #Position im Code
    codeindex=0
    #Klammernpaare finden für Schleifen
    offenposition, zuposition= Klammernpaare(code)

    while codeindex < len(code):
        index=code[codeindex]
        if index==">":
            cellindex +=1
           
        elif index=="<":
            cellindex -=1
            
        elif index=="+":
            cells[cellindex] +=1
           
        elif index=="-":
            cells[cellindex] -=1
            
        elif index==".":
            print(chr(cells[cellindex]), end="")
         

        elif index==",":
            cells[cellindex] = ord(input())
        

        elif index=="[":
            if cells[cellindex]== 0:
                codeindex=zuposition[codeindex]
                
                

        elif index=="]":
            codeindex=offenposition[codeindex]
            codeindex -=1
                        
        codeindex +=1
        
                
                
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

@darktrym: Weder noch würde ich sagen, sondern einfach ein Byte von der Standardeingabe lesen wie das Sirius3 weiter vorne im Thema schon mal in einem BF nach Python-Übersetzer gezeigt hat. So machen das jedenfalls alle anderen BF-Implementierungen die ich kenne. Man kann ja kein Terminal voraussetzen und die Eingaben müssen ja nicht zwingend aus Tastendrücken bestehen.
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

@BlackJack: Ich steh' grad auf dem Schlauch. Das("sys.stdin.read(1)") verhält sich doch wie raw_input unter Windows, Abschluss der Eingabe mit einem Enter. Und gerade das war nicht gewollt?
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

@darktrym: Nein, so verhält sich das ``sys.stdin.read(1)`` nicht. Das liest genau ein Byte von der Standardeingabe des Prozesses. Das was Du beobachtest ist wahrscheinlich das Verhalten des Terminals was die Eingabe erst an den Prozess weiterleitet, wenn Du entweder das Zeichen für eine neue Zeile durch die Eingabetaste erzeugst oder den Datenstrom mit der entsprechenden Tastenkombination beendest. Unter Unix ist das Ctrl+D und unter Windows Ctrl+Z. Auf jeden Fall ist das das Verhalten was alle anderen (mit bekannten) BF-Interpreter an den Tag legen und was auch Sinn macht. Denn man möchte Ein- und Ausgabe in der Regel umleiten können.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Ich habe versucht es mit einem Dictionary umzusetzen aber nach eine Schleife beendet es mein Programm.

Code: Alles auswählen

import sys

def bf(code):
    def Klammernpaare(code):
        listeoffen=[]
        zuposition={}
        for s in code:
            if s=="[":
                listeoffen.append(code.index(s))
            elif s=="]":
                zuposition[listeoffen.pop()]=code.index(s)

        offenposition=dict((zuposition[i],i) for i in zuposition)    
	
        return offenposition, zuposition
		
    def ink():
        cells[cellindex] +=1
    def dek():
        cells[cellindex] -=1
    def vorne():
        cellindex +=1
    def hinten():
        cellindex -=1
    def ausgabe():
        print(chr(cells[cellindex]), end="")
    def eingabe():
        cells[cellindex] = ord(sys.stdin.read(1))  #ord(input())
    def schleifenanfang():
        if cells[cellindex]== 0:
                codeindex=zuposition[codeindex]
    def schleifenende():
        codeindex=offenposition[codeindex]
        codeindex -=1
    befehle={"+":ink,"-":dek,">":vorne,"<":hinten,".":ausgabe,",":eingabe,"[":schleifenanfang,"]":schleifenende}
    #Kommentare entfernen
    code=[s for s in code if s in ("<",">","+","-",".",",","[","]")]
    #aktuelle Zelle
    cellindex=0
    #Zellen
    cells=[0]*30000
    #Position im Code
    codeindex=0
    #Klammernpaare finden für Schleifen
    offenposition, zuposition= Klammernpaare(code)

    while codeindex < len(code):
        index=code[codeindex]
        befehle[code[codeindex]]()
        codeindex +=1


bf(input("Geben sie hier ihren Code ein: "))
input()
        
                
                
Antworten