From 0f15e187e13689195018651b21be1b913b6132e9 Mon Sep 17 00:00:00 2001 From: Jonas Weinz Date: Mon, 12 Sep 2016 15:22:50 +0200 Subject: [PATCH] initial commit --- README.md | 1 + TuringInterpreter.py | 125 +++++++++++++++++++++++ Turingmachine.py | 200 +++++++++++++++++++++++++++++++++++++ add.tur | 106 ++++++++++++++++++++ nachfolger.tur | 15 +++ testProgram.tur | 108 ++++++++++++++++++++ testProgramUncommented.tur | 42 ++++++++ 7 files changed, 597 insertions(+) create mode 100644 README.md create mode 100755 TuringInterpreter.py create mode 100755 Turingmachine.py create mode 100755 add.tur create mode 100755 nachfolger.tur create mode 100755 testProgram.tur create mode 100644 testProgramUncommented.tur diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8909e1 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Kommentiertes Beispielprogramm ist in der Datei testProgram.tur diff --git a/TuringInterpreter.py b/TuringInterpreter.py new file mode 100755 index 0000000..dbdbdae --- /dev/null +++ b/TuringInterpreter.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +from Turingmachine import * +import sys + +def getLinesFromFile(filename): + content = [] + with open(filename, "r") as f: + content = f.read().splitlines() + + return content + +def createTuringmachineFromLines(lineArray, userInput): + k = None + F = None + S = None + I = None + + inputStr = userInput + + d = [] + + lineCounter = 0 + + for line in lineArray: + lineCounter += 1 + + line = line.replace(" ","") + + if len(line) > 0: + if line[0] == 'k' and line[-1] == ';': + #hole k: + l = line[1:-1] #entferne k und ; + kStr = l.replace("=","") #entferne whitespaces und = + k = int(kStr) + + elif line[0] == 'F' and line[-1] == ';': + #hole Endzustände: + l = line[1:-1] #entferne F und ; + FStr = l.replace("=","") #entferne whitespaces und = + F = FStr.split(",") + + elif line[0] == 'S' and line[-1] == ';': + #hole Startzustand: + l = line[1:-1] #entferne S und ; + S = l.replace("=","") #entferne whitespaces und = + elif line[0] == 'I' and line[-1] == ';': + #hole Input: + l = line[1:-1] #entferne S und ; + I = l.replace("=","") #entferne whitespaces und = + elif line[0] == '$' and line[-1] == ';': + #hole Zustandsübergang: + l = line[1:-1] #entferne $ und ; + tmp = l.replace("=","") #entferne whitespaces und = + splittedLine = tmp.split(",") + + #parse orientations: + orientations = [] + for i in range(len(splittedLine[4])): + if splittedLine[4][i] == 'l': + orientations.append (-1) + elif splittedLine[4][i] == 'r': + orientations.append (1) + elif splittedLine[4][i] == 'n': + orientations.append (0) + else: + print("ERROR in line " + str(lineCounter) + ": " + line) + return None + d.append((splittedLine[0],splittedLine[1],splittedLine[2],splittedLine[3],orientations)) + + elif line[0] == '#': + #bei Kommentaren tu nix + None + else: + print("ERROR in line " + str(lineCounter) + ": " + line) + return None + + if k is None: + print("ERROR: no k defined in Turing Program!") + return None + if F is None: + print("ERROR: no F defined in Turing Program!") + return None + if S is None: + print("ERROR: no S defined in Turing Program!") + return None + + if inputStr is None: + if I is None: + print("WARNING: there is no input string defined. Machine will use an empty input string") + inputStr = "" + else: + inputStr = I + + + + return Turingmachine(d, S, F, k, inputStr) + +if __name__ == "__main__": + try: + filename = sys.argv[1] + except: + print("ERROR: no input file. \n" + + "usage: python TuringInterpreter.py [TuringProgramFile] [InputString] [SleepTimePerStep] \n" + + "(Default value for SleepTimePerStep is 0.3)") + sys.exit(0) + + try: + inputStr = sys.argv[2] + except: + inputStr = None + + try: + sleepTime = float(sys.argv[3]) + except: + sleepTime = 0.3 + + tm = createTuringmachineFromLines(getLinesFromFile(filename), inputStr) + + if tm is None: + print("ERROR: Bad Program!") + sys.exit(0) + + tm.run(sleepTime) + + diff --git a/Turingmachine.py b/Turingmachine.py new file mode 100755 index 0000000..56262a3 --- /dev/null +++ b/Turingmachine.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +import numpy as np +import sys +import time + +# Bezeichnungen: +# delta = Zustandsübergangsfunktion: eine Liste von Tupeln im Format: +# (aktuellerZustand, aktuelleZeichen[Tupel], nächsterZustand, zuSchreibendeZeichen[Tupel], orientation[Tupel]) +# q0 = Startzustand +# F = Menge der Endzustände + +#ein paar konstanten: +GO_LEFT = -1 +GO_NOWHERE = 0 +GO_RIGHT = 1 + +#zur vereinfachung: konstanten für indices in delta: +DELTA_CURRENT_STATE = 0 +DELTA_CURRENT_CHARS = 1 +DELTA_NEXT_STATE = 2 +DELTA_NEXT_CHARS = 3 +DELTA_ORIENTATIONS = 4 + +#Konstanten für die Ausgabe: +CURSOR_UP_ONE = '\x1b[1A' +ERASE_LINE = '\x1b[2K' + +#hilfsfunktionen für die ausgabe: +def eraseLines(n): + for i in range(n): + print(CURSOR_UP_ONE + ERASE_LINE, end = "") + +#und die eigentliche Turingmaschine: +class Turingmachine(object): + def __init__(self,delta, q0, F, n, inputStr): + self.delta = delta + self.q0 = q0 + self.F = F + + self.n = n + + #zur internen Verwaltung der Turinmaschine brauchen wir folgendes: + self.stripes = [] + self.positions = [] + + self.steps = 0 #counting the steps + + for i in range(n): + self.stripes.append(['$']) + self.positions.append(0) + + #schreibe unsere Eingabe auf das erste Band: + for c in inputStr: + self.stripes[0].append(c) + + self.currentQ = q0 + self.error = False + self.isInFinalState = False + + self.firstPrint = True + + # Lese char unter dem i-ten Lesekopf: + def readChar(self,i): + + #erweitere Band, falls notwendig: + if len(self.stripes[i]) <= self.positions[i]: + self.stripes[i].append('_') + + #und Zeichen zurückgeben: + return self.stripes[i][self.positions[i]] + + # Schreibe char unter dem i-ten Lesekopf: + def writeChar(self,i, char): + + #erweitere Band, falls notwendig: + if len(self.stripes[i]) <= self.positions[i]: + self.stripes[i].append('_') + + #und setze Zeichen: + self.stripes[i][self.positions[i]] = char + + #bewege den Lese-/Schreibkopf + def move(self, i, orientation): + self.positions[i] += orientation + + #führe einen Zustandsübergang aus: (gibt true bei erfolg zurück, sonst false) + def runDelta(self): + + #durchlaufe alle Übergänge in Delta + for d in self.delta: + + #suche nach Übergängen für unseren Zustand: + if d[DELTA_CURRENT_STATE] == self.currentQ: + + #prüfe Einganszeichen: + qInputChars = d[DELTA_CURRENT_CHARS] + isInputOK = True + for i in range(self.n): + if not ((qInputChars[i] == self.readChar(i)) or (qInputChars[i] == '*')): + #eingangszeichen stimmen nicht überein! + isInputOK = False + + if isInputOK: + #jetzt haben wir den richtigen zustandsübergang gefunden! + qNextChars = d[DELTA_NEXT_CHARS] + qOrientations = d[DELTA_ORIENTATIONS] + for i in range(self.n): + #schreibe Zeichen: + if qNextChars[i] != '*': + self.writeChar(i, qNextChars[i]) + #Bewege Lese-/Schreibkopf + self.move(i, qOrientations[i]) + + #prüfe auf underflows: + for j in range(self.n): + if self.positions[j] < 0: + return False + + #setze nächsten Zustand: + self.currentQ = d[DELTA_NEXT_STATE] + self.steps+=1 + + #fertig! + return True + + #wenn wir bis hierhin kommen, haben wir keinen Übergang gefunden! + return False + + # mache einen einzelnen Schritt mit der Turing Maschine (gibt true bei erfolg zurück, sonst false) + def step(self): + #führe den Schritt nuraus, wenn wir noch nicht im Endzustand sind: + if not self.isInFinalState: + self.error = not self.runDelta() + + #schaue, ob wir in einem Endzustand sind: + self.isInFinalState = (self.currentQ in self.F) + + return self.error + return True + + def printMachine(self): + if not self.firstPrint: + #Lösche voherige Ausgabe: + eraseLines(2 * self.n + 1) + self.firstPrint = False + #geben bänder aus: + for i in range(self.n): + print("[",end="") + for c in self.stripes[i]: + print(c,end = "") + print("]") + #drucke lese-/Schreibkopf + print (" " * (self.positions[i] +1) + "^") + + #und gebe allgemeine Infos aus: + print("step: " + str(self.steps) + "\t currentState: " + str(self.currentQ)) + + #run the machine until it gets to a final state: + def run(self, delayInSeconds): + while not self.isInFinalState: + self.step() + self.printMachine() + if self.error: + print("ERROR! Please check your Turing Program") #TODO: aussagekräftigere Fehlermeldung? + break + time.sleep(delayInSeconds) + if not self.error: + self.printMachine() + print("Maschine terminatet!") + +if __name__ == "__main__": + + #Blatt13/Aufgabe01 zum testen: + + F = ['finalState'] + d = [('start',"$$$$",'init',"$$$$",(1,1,1,1)), + ('init',"****",'a',"*!!!",(0,1,1,1)), + ('a',"a***",'a',"aa**",(1,1,0,0)), + ('a',"b***",'b',"b***",(0,0,0,0)), + ('b',"b***",'b',"b*b*",(1,0,1,0)), + ('b',"c***",'c',"c***",(0,0,0,0)), + ('c',"c***",'c',"c**c",(1,0,0,1)), + ('c',"_***",'counting',"_***",(-1,-1,-1,-1)), + ('counting',"*abc",'counting',"*abc",(0,-1,-1,-1)), + ('counting',"*!**",'checkCounter',"*!**",(0,0,0,0)), + ('counting',"**!*",'checkCounter',"**!*",(0,0,0,0)), + ('counting',"***!",'checkCounter',"***!",(0,0,0,0)), + ('checkCounter','*!!!','wordAccepted','*!!!',(0,0,0,0)), + ('checkCounter','****','wordNotAccepted','****',(0,0,0,0)), + ('wordAccepted','$***','write1','$***',(1,0,0,0)), + ('wordAccepted','****','wordAccepted','_***',(-1,0,0,0)), + ('wordNotAccepted','$***','write0','$***',(1,0,0,0)), + ('wordNotAccepted','****','wordNotAccepted','_***',(-1,0,0,0)), + ('write0','****','finalState','0***',(0,0,0,0)), + ('write1','****','finalState','1***',(0,0,0,0)) + ] + + tm = Turingmachine(d, 'start', F, 4, "aaaaaaaaabbbbbbbbbccccccccc") + + tm.run(0.15) diff --git a/add.tur b/add.tur new file mode 100755 index 0000000..674b721 --- /dev/null +++ b/add.tur @@ -0,0 +1,106 @@ +#!./TuringInterpreter.py +# +#Ein Turingprogramm, das zwei Binärcodierte zahlen +#miteinander addiert + +k = 4; +S = start; +F = end; +I = 101001+110; + + +#init: +$start,$$$$,readFirst,$$$$,rrrr; + +#lese erste Zahl ein und schreibe die auf das 2. Band: +$readFirst,1***,readFirst,11**,rrnn; +$readFirst,0***,readFirst,00**,rrnn; +$readFirst,+***,readSecond,****,rnnn; + +#lese zweite Zahl ein und schreibe die auf das 3. Band: +$readSecond,1***,readSecond,1*1*,rnrn; +$readSecond,0***,readSecond,0*0*,rnrn; +$readSecond,_***,clear,****,nnnn; + +#Lösche das erste band: +$clear,$***,add,$***,rlln; +$clear,****,clear,_***,lnnn; + +#------------------------------------------------------------------------------- + +#und addiere. Konkret: laufe auf Band 2 und 3 rückwärts die Zahlen +# ab und schreibe das ergebnis in rückwärtiger Schreibweise +# auf das dritte Band. +# dabei gibt es folgende Möglichkeiten: + +# 1. wir haben bei beiden inputs gerade eine null und keinen Übertrag +# vorher gehabt: + +$add,*00_,add,*000,nllr; + +# 2. wir haben bei beiden inputs gerade eine null und haben einen Übertrag: +$add,*001,add,*001,nllr; + +# 3. beide inputs verschieden ohne übertrag vorher: + +$add,*10_,add,*101,nllr; +$add,*01_,add,*011,nllr; + +# 4. beide inputs verschieden mit übertrag vorher: + +$add,*101,overflow,*100,nllr; +$add,*011,overflow,*010,nllr; + +# 5. beide inputs gleich 1 ohne übertrag: + +$add,*11_,overflow,*110,nllr; + +# 6. beide inputs 1 mit übertrag vorher: +$add,*111,overflow,*111,nllr; + +# 7. wir sind am ende von einem band (dann juss nur noch der inhalt vom anderen +# angehängt werden): +$add,*$**,addSecondInput,*$**,nnnn; +$add,**$*,addFirstInput,**$*,nnnn; + +#------------------------------------------------------------------------------- + +# anhängeFunktionen: +#sind wir fertig? +$addSecondInput,**$*,copyResult,**$*,nnnn; +#haben wir overflow? +$addSecondInput,**01,addSecondInput,**01,nnlr; +$addSecondInput,**11,overflow2,**10,nnlr; +#sonst: +$addSecondInput,**0_,addSecondInput,**00,nnlr; +$addSecondInput,**1_,addSecondInput,**11,nnlr; + +#------------------------------------------------------------------------------- + +# anhängeFunktionen: +#sind wir fertig? +$addFirstInput,*$**,copyResult,*$**,nnnn; +#haben wir overflow? +$addFirstInput,*0*1,addFirstInput,*0*1,nlnr; +$addFirstInput,*1*1,overflow1,*1*0,nlnr; +#sonst: +$addFirstInput,*0*_,addFirstInput,*0*0,nlnr; +$addFirstInput,*1*_,addFirstInput,*1*1,nlnr; + +#------------------------------------------------------------------------------- + +#overflow funktionen: + +$overflow,****,add,***1,nnnn; +$overflow1,****,addFirstInput,***1,nnnn; +$overflow2,****,addSecondInput,***1,nnnn; + +#------------------------------------------------------------------------------- + +#und schreibe Ergebnis auf das erste Band: + +$copyResult,***$,end,***$,nnnn; +$copyResult,***1,copyResult,1**1,rnnl; +$copyResult,***0,copyResult,0**0,rnnl; +$copyResult,***_,copyResult,***_,nnnl; + diff --git a/nachfolger.tur b/nachfolger.tur new file mode 100755 index 0000000..4e3bdb6 --- /dev/null +++ b/nachfolger.tur @@ -0,0 +1,15 @@ +#!./TuringInterpreter.py +k = 1; +S = start; +F = end; +I = 0100101011; + +$start,$,runToEnd,$,r; + +#laufe ans ende der eingabe: +$runToEnd,_,addOne,_,l; +$runToEnd,*,runToEnd,*,r; + +#addiere eins: +$addOne,0,end,1,l; +$addOne,1,addOne,0,l; diff --git a/testProgram.tur b/testProgram.tur new file mode 100755 index 0000000..c251d59 --- /dev/null +++ b/testProgram.tur @@ -0,0 +1,108 @@ +#!./TuringInterpreter.py +# +# dies ist ein Beispielprogramm für die Turingmaschine +# (Hinweis: Kommentarzeilen können mit einer führenden Raute eingeleitet werden) +# +# Dieses Programm kann mit dem Python-Skript "TuringInterpreter.py" für einen beliebigen Input-String via +# +# "python TuringInterpreter.py " +# +# ausgeführt werden. der InputString wird dabei auf das oberste Band geschrieben +# +# Folgendes Turingprogramm prüft, ob eine eingabe Element +# der Sprache L = {a^n b^n c^n} ist. Es schreibt eine 1 +# auf das erste Band, falls das Wort akzeptiert wird, +# und 0 sonst. + +# definiere die Anzahl der Bänder: + +k = 4; + +# definiere Endzustand. Es können alternativ auch mehrere Enzustände (durch Kommata getrennt) +# angegeben werden + +F = finalState; + +# definiere Startzustand + +S = start; + +# definiere einen default input String, der benutzt wird, falls beim Programmaufruf +# keiner angegeben wird: + +I = aaaaaabbbbbbcccccc; + + +# definiere Zustandsübergänge: +# Ein Zustandsübergang wird mit einem '$' eingeleitet und besteht durch Kommata getrennt aus folgenden Werten: +# +# 1. Ausgangszustand +# +# 2. Zeichen unter den Lese-/Schreibköpfen vor dem Zustandswechsel. +# '*' kann hier als Platzhalter genutzt werden, um jedes beliebige Zeichen zu akzeptieren +# +# 3. Zustand, zu dem gewechselt werden soll +# +# 4. Zeichen, die auf die Bänder geschrieben werden sollen. +# '*' bewirkt hier, dass kein Zeichen geschrieben wird +# (oder anders interpretiert: Es wird exakt das Zeichen geschrieben, +# das vorher gelesen wurde) +# +# 5. Anschließende Bewegungen der Lese-/Schreibköpfe. Zu verwenden ist folgende Notation: +# +# n: keine Bewegung +# l: Bewegung um ein Feld nach links +# r: Bewegung um ein Feld nach rechts +# +# Und noch ein Hinweis: die Turingmaschine sucht in der Reihenfolge nach dem richtigen Übergang, +# wie sie in der Programmdatei angegeben wurden. Das lässt sich in Verbindung mit Platzhaltern +# prima nutzen um if-else verzweigungen zu generieren, ohne dass dabei alle theoretisch denkbaren +# Zeichenkombinationen als eigener Zustandsübergang angegeben werden müssen ;) + + +# am Anfang stehen alle Köpfe auf dem $-Zeichen, dass immer am Anfang jedes Bandes steht, also +# bewegen wir alle Köpfe einen Schritt nach rechts: +$start, $$$$, init , $$$$, rrrr; + +# die unteren drei köpfe schreiben ein !. (um nachher zu sehen, wo sie starteten): +$init,****,a,*!!!,nrrr; + +# lese alle a-s auf dem ersten Band ein: (und schreibe exakt so viele auf das zweite Band): +$a , a*** , a , aa** ,rrnn; +$a , b*** , b , b*** ,nnnn; +$a , **** , badInput, ****, nnnn; + +# verfahre genauso mit b: +$b , b*** , b , b*b* ,rnrn; +$b , c*** , c , c*** ,nnnn; +$b , **** , badInput, ****, nnnn; + +# und mit c: +$c , c*** , c , c**c ,rnnr; +$c , _*** , counting , _*** ,llll; +$c , **** , badInput, ****, nnnn; + +# lasse die unteren drei köpfe zurücklaufen, bis einer auf das ! trifft: +$counting , *abc , counting , *abc , nlll; +$counting , *!** , checkCounter , *!** , nnnn; +$counting , **!* , checkCounter , **!* , nnnn; +$counting , ***! , checkCounter , ***! , nnnn; + +# sind alle beim Ausrufezeichen? +$checkCounter , *!!! , wordAccepted,*!!!,nnnn; +$checkCounter , **** , wordNotAccepted,****,nnnn; + +# falls ja: löschen wir alles auf dem ersten Band und schreiben eine 1 +$wordAccepted , $*** , write1,$***,rnnn; +$wordAccepted , **** , wordAccepted,_***,lnnn; +$write1 , **** , finalState , 1*** ,nnnn; + +# falls nein: löschen wir alles auf dem ersten Band und schreiben eine 0 +$wordNotAccepted , $*** , write0 , $*** ,rnnn; +$wordNotAccepted , **** , wordNotAccepted , _*** ,lnnn; +$write0 , **** , finalState , 0*** ,nnnn; + +# hilfsfunktion, um das Band auch nach vornehin zu löschen: +$badInput, _***, wordNotAccepted, _****, nnnn; +$badInput, ****, badInput, _***, rnnn; + diff --git a/testProgramUncommented.tur b/testProgramUncommented.tur new file mode 100644 index 0000000..d76c4b0 --- /dev/null +++ b/testProgramUncommented.tur @@ -0,0 +1,42 @@ +#!./TuringInterpreter.py +# +# dies ist ein Beispielprogramm für die Turingmaschine +# (Hinweis: Kommentarzeilen können mit einer führenden Raute eingeleitet werden) +# +# Dieses Programm kann mit dem Python-Skript "TuringInterpreter.py" für einen beliebigen Input-String via +# +# "python TuringInterpreter.py " +# +# ausgeführt werden. der InputString wird dabei auf das oberste Band geschrieben +# +# Folgendes Turingprogramm prüft, ob eine eingabe Element +# der Sprache L = {a^n b^n c^n} ist. Es schreibt eine 1 +# auf das erste Band, falls das Wort akzeptiert wird, +# und 0 sonst. + +k = 4; +F = finalState; +S = start; +I = aaaaaabbbbbbcccccc; + +$start, $$$$, init , $$$$, rrrr; +$init,****,a,*!!!,nrrr; +$a , a*** , a , aa** ,rrnn; +$a , b*** , b , b*** ,nnnn; +$b , b*** , b , b*b* ,rnrn; +$b , c*** , c , c*** ,nnnn; +$c , c*** , c , c**c ,rnnr; +$c , _*** , counting , _*** ,llll; +$counting , *abc , counting , *abc , nlll; +$counting , *!** , checkCounter , *!** , nnnn; +$counting , **!* , checkCounter , **!* , nnnn; +$counting , ***! , checkCounter , ***! , nnnn; +$checkCounter , *!!! , wordAccepted,*!!!,nnnn; +$checkCounter , **** , wordNotAccepted,****,nnnn; +$wordAccepted , $*** , write1,$***,rnnn; +$wordAccepted , **** , wordAccepted,_***,lnnn; +$write1 , **** , finalState , 1*** ,nnnn; +$wordNotAccepted , $*** , write0 , $*** ,rnnn; +$wordNotAccepted , **** , wordNotAccepted , _*** ,lnnn; +$write0 , **** , finalState , 0*** ,nnnn; +