Befunge Compiler in Python

In Vorbereitung auf den Kurs "Fundamentals of Compilers" für Studenten im 4. Jahr habe ich verschiedene esoterische Programmiersprachen studiert. Hier ist ein guter Artikel zu diesem Thema . Die Befunge-Sprache (Chris Press, 1993) schien mir die interessanteste in dem Artikel zu sein. Ich stelle insbesondere drei ihrer Merkmale fest:

  1. Das Programmfeld ist ein zweidimensionaler Torus, d.h. Physikalisch ist dies eine rechteckige Matrix von Symbolbefehlen, die entlang der oberen (unteren) Grenze und entlang der linken (rechten) Spalte geschlossen sind. Der Befehlszeiger bewegt sich im Feld (jeder Befehl ist ein bestimmtes Zeichen mit x-, y-Koordinaten), führt den Befehl aus und fährt fort. Die Bewegung kann in alle 4 Richtungen erfolgen (standardmäßig direkt ab dem Punkt 0,0). Wenn Sie über das "Feld" hinausgehen, wird der Zeiger auf der gegenüberliegenden Seite angezeigt.
  2. Es gibt zwei Befehle (p, g) in der Sprache, die das Feld selbst ändern, d.h. Das Programm wird während der Ausführung "selbst umgeschrieben". Der Programmcode am Anfang entspricht möglicherweise nicht dem Code am Ende. Das Programm "123pgpg ## @" wurde gestartet und das Programm "ABC @ 1 @ 2 @ 3.14" (kein korrektes Beispiel) wurde beendet.
  3. Chris Pressy bemerkte, dass er eine Sprache erstellen wollte, deren Kompilierung so komplex wie möglich war. De facto, es ist wahr, als ich einen Compiler erstellte, der exe-Dateien für das Programm höllisch schwierig macht, fand ich Informationen, dass jemand dies in C tun könnte ... Es ist am besten, einen Übersetzer von der Sprache in den Python-Code zu erstellen, den ich immer noch aufrufe Compiler der Einfachheit halber.


Das Programmfeld von 1993 besteht aus 25 Zeilen mit jeweils 80 Zeichen. Es gibt 36 Teams in der Sprache, von denen jedes ein ASCII-Tabellenzeichen ist. Weitere Informationen finden Sie in Wikipedia. Von dort aus werde ich eine kurze Beschreibung geben:

Verschieben von Befehlen (9):

> Nach rechts verschieben
<Nach links verschieben
^ Nach oben verschieben
v Nach unten verschieben
_ Nach rechts verschieben, wenn die Oberseite des Stapels 0 ist, andernfalls nach links.
| Bewegen Sie sich nach unten, wenn Sie sich auf Stapel 0 befinden, andernfalls nach oben.
? Bewegen Sie sich in eine zufällige Richtung
# Überspringen Sie die nächste Zelle („Sprungbrett“)
@ Ende des Programms

Stapelmanipulationsbefehle (3)

:: Legen Sie eine Kopie des Scheitelpunkts auf den Stapel
\ Scheitelpunkt und Scheitelpunkt
tauschen $ Scheitelpunkt

löschen Befehle Änderung des Programmcodes (2):
p „PUT“: Zellkoordinaten und ASCII-Code des Zeichens, das an diesen Koordinaten platziert wird,
werden aus dem Stapel extrahiert. G „GET“: Zellkoordinaten werden aus dem Stapel extrahiert. Der ASCII-Code eines Symbols an diesen Koordinaten wird auf den Stapel geschoben .

Konstante Befehle (2):

0-9 Platzieren Sie eine Zahl auf dem Stapel
Start / Ende des Symbolmodus , in der ASCII-Codes aller aktuellen Programmzeichen

auf den Stapel geschoben werden. Arithmetische Operationen (5):

+ Hinzufügen eines Scheitelpunkts und eines Scheitelpunkts
- Subtrahieren eines Scheitelpunkts und eines Scheitelpunkts
* Multiplizieren eines Scheitelpunkts und einer Scheitelpunkt-
/ Ganzzahldivision
% Verbleibende Division

Befehle für den Stapel und logische Operationen (2)

:! Negation: Null auf dem Scheitelpunkt wird durch 1 ersetzt, Wert ungleich Null wird durch 0 ersetzt.
Vergleich „größer als“: Wenn der Scheitelpunkt größer als der Scheitelpunkt ist, wird 1 auf den Stapel gelegt, andernfalls 0

E / A-Befehle (4):

& Fordern Sie den Benutzer nach einer Zahl auf und platzieren Sie diese auf dem Stapel
~ Bitten Sie den Benutzer um ein Zeichen und geben Sie seinen ASCII-Code auf den Stapel
. Drucken Sie die Oberseite des Stapels als Ganzzahl
. Drucken Sie das Zeichen, das dem ASCII-Code entspricht, oben auf dem Stapel

Ich habe beschlossen, einen Befunge-Compiler (Interpreter) in Python nach den Regeln von 1993 mit einigen Einschränkungen zu schreiben: 1) Das Feld hat keine 25x80 Zeichen, aber das Minimum an Breite und Höhe des Textblocks. 2) Das Feld ist nicht in einen Torus eingeschleift, d. H. Das Überschreiten von Grenzen mit dem Springen auf die gegenüberliegende Seite wird nicht verarbeitet. Dies ist keine Faulheit (wen scherze ich?). Für kleine Beispiele funktioniert alles gut, und das Feld zu einem echten Torus zu beenden, ist ganz einfach, es würde einen Wunsch geben.

Der Code kam an einigen Stellen unnötig „auf der Stirn“ heraus, aber dies liegt an der Tatsache, dass er für Studenten ist und seine Aufgabe darin besteht, so klar wie möglich zu sein und nicht auf ein paar Zeilen auf Chinesisch gekürzt zu werden.

Teil 1


Der Code wird von Anfang bis Ende mit einer Ausnahme bereitgestellt (die angegeben wird). Er kann in eine Datei kopiert und ausgeführt werden. Der vollständige Text ist unter dem Link rentry.co/ivansedov-befunge verfügbar. Für mich ist es noch zu früh, ihn auf GitHub zu stellen. Übrigens gibt es dort ungefähr 20 Befunge-Sprachimplementierungen, aber der Code ist entweder in C (nicht meine Sprache) oder Python, aber so kompliziert, dass ich mich nicht traute zu tauchen. Dort können Sie jedoch Beispielprogramme für Tests verwenden, z. B. hier github.com/causal-agent/befungee

from sys import *
import time
import random

Importieren Sie die Bibliotheken:

  1. Die sys-Bibliothek wird benötigt, um den Namen der Programmdatei zu erhalten. Mein Compiler hieß bbb.py, ein Testbeispiel im selben Verzeichnis 1.bf, Python Version 3.7 selbst, und so sah der Programmaufruf in der Konsole folgendermaßen aus: python3 bbb.py 1.bf
  2. time , , 0,5-1,0 .
  3. random «?» ( ), . -, Befunge - , « » (1 , 2 , 3 , 4 ). « » .

class Pointer:
    def __init__(self, x=0, y=0, vector=2, value=None):
        self.x = x
        self.y = y
        self.vector = vector
        self.value = value
        self.stack = []
        self.stack_sf = 0

    def __str__(self):
        return 'Point ({},{}) vektor:{} value:{} stack_sf:{} stack:{}'.format(self.x, self.y, self.vector, self.value, self.stack_sf, self.stack)

    def step(self):
        if self.vector == 1:
            self.x -= 1
        elif self.vector == 2:
            self.y += 1
        elif self.vector == 3:
            self.x += 1
        elif self.vector == 4:
            self.y -= 1

    def action(self):
	#   ,   

Die im Programm verwendete Hauptklasse: Zeiger (Zeiger) hat 6 Eigenschaften und 2 Methoden. Eigenschaften: 1) x-Koordinate (anfänglich = 0), 2) y-Koordinate (anfänglich = 0), 3) Vektor (Bewegungsrichtung, anfänglich = 2 nach rechts), 4) Wert (Feldwert, der sich an der x-, y-Koordinate befindet, anfänglich = keine), dies ist tatsächlich der auszuführende Befehl, 5) Stapel (Programmstapel, anfänglich = []) und 6) Stapel_st (Flag zum Eingeben von Zeilen, anfänglich = 0).

Die Klasse verfügt über zwei Methoden: 1) step () Der Zeigerschritt ändert ohne Überprüfungen und Tests die x- und y-Koordinaten in Abhängigkeit von der Richtung im Vektor und 2) action () ist das Herzstück des Compilers und führt den aktuellen Programmbefehl aus. Ich werde den action () - Code im zweiten Teil geben, damit er nicht von der Logik ablenkt.

# ===============================
# =                      =
# ===============================

def open_file(name):
    data = open(name, 'r').read()
    return data

def field_print():
    for row in A:
        for elem in row:
            print(elem, end='')
        print()  

Zwei Hilfsfunktionen: 1) open_file (name) öffnet die Datei mit dem übermittelten Namen, liest sie und gibt den Inhalt zurück, und 2) field_print () druckt den Inhalt von Array A, in dem sich die Programmzeichen befinden. Das Erstellen von Array A wird unten gezeigt.

# ===========================================
# =                       =
# ===========================================

numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
operators = ['+', '-', '*', '/', '%']
point = Pointer()                           #  
text = open_file(argv[1]).split("\n")       #  
n = len(text)                               # n =  
m = 0                                       # m =  

#    (   m)
for line in text:
    if len(line) > m:
        m = len(line)

#   ( n  m )
A = [' '] * n
for i in range(n):
    A[i] = [' '] * m

#    
for i in range(len(text)):
    for j in range(len(text[i])):
        A[i][j] = text[i][j]

Grundlegende Programmeinstellungen. In die Zahlenliste setzen wir alle Zahlen von 0 bis 9, die in das Programm geschrieben werden können (10 passen nicht mehr, zwei Zeichen). In die Liste der Operatoren setzen wir die grundlegenden arithmetischen Operatoren. Erstellen Sie ein point = Pointer-Objekt (Standardeinstellungen), mit dem wir die ganze Zeit arbeiten werden ...

In den variablen Text setzen wir den gelesenen Text des ausführbaren Programms, unterbrochen durch das Symbol "Neue Zeile". Infolgedessen besteht Text aus mehreren Textzeilen unterschiedlicher Länge, die an ihren Stellen in einer rechteckigen Anordnung von Leerzeichen platziert werden müssen. Es ist leicht, die Anzahl der Zeilen n = len (Text) zu ermitteln, aber die Anzahl der Spalten muss basierend auf der maximalen Länge der im Text enthaltenen Zeilen berechnet werden. Ich habe keinen anderen Weg gefunden, um diese Stirn zu machen: gehe alle Linien durch und finde die mit der maximalen Länge. Wenn Sie n und m (die Anzahl der Zeilen und die Anzahl der Spalten des zukünftigen Felds) zur Hand haben, können Sie ein zweidimensionales Array erstellen, es mit Leerzeichen füllen und dann den Text durchgehen, um die Zeichen an ihren Stellen zu platzieren.

Das Ergebnis ist ein Rechteck aus Zeichen, zwischen denen Leerzeichen stehen, und all dies ist eine zweidimensionale Matrix n mal m. Nach diesen Einstellungen können Sie die Funktion field_print () aufrufen und sicherstellen, dass alles schön aussieht, nichts schwebt und die Situation nicht verletzt hat.

# ==========================================
# =                       =
# ==========================================

# field_print()

while point.value != '@':
    try:
        point.value = A[point.x][point.y]       # 1)    point
        point.action()                          # 2)  
        # print(point)                            # 3) :  
        # time.sleep(0.5)                         # 4) :  0.5 c
    except IndexError:                          #     =  
        print('     ')
        break

# field_print()
print()

Alles endet mit dem Hauptprogramm (Zyklus), vor und nach dem Sie das Feld anzeigen können (manchmal nützlich). Der Zyklus dreht sich, bis der Zeiger auf das Symbol „@“ zeigt (kaufmännisches Und, Hund, Programmende). Innerhalb des Zyklus werden jedes Mal 4 Aktionen ausgeführt:

  1. In der Eigenschaft point.value wird das Zeichen gelesen, das sich in Array A an den Koordinaten point.x, point.y befindet
  2. Die Methode point.action () wird aufgerufen, in der der aktuelle (gerade gelesene) Befehl ausgeführt wird
  3. Auf dem Bildschirm wird ein Zeiger angezeigt (alle Eigenschaften)
  4. Vor der nächsten Iteration wird eine Verzögerung vorgenommen (0,1 s - 0,5 s).

Die Punkte 3 und 4 sind völlig optional (sie sind sogar auskommentiert), aber ich empfehle, sie zum Testen zu verwenden. Alle Aktionen innerhalb der Schleife werden ausgeführt, wenn ein IndexError-Fehler abgefangen wird (Fehler, der die Grenzen des Index überschreitet). Auf diese Weise können Sie zwei Haupt-Compilerfehler abfangen:

  1. Wir haben uns dem Stapel zugewandt, und es gibt keine Werte
  2. Wir sind versehentlich in Breite oder Höhe über das Programm (Array) hinausgegangen

Das letzte leere print () wird benötigt, damit die Konsole nach der Anzeige der Ausgabe in einer neuen Zeile arbeitet.

Teil 2


Jetzt ist es Zeit für den wichtigsten Code - den Inhalt der point.action () -Methode der Pointer-Klasse. Alles unten sollte dort eingefügt werden, wo es geschrieben wurde:

    def action(self):
	#   ,   

Achten Sie auf die Einrückung:

        if self.value == '"' and self.stack_sf == 0:                # ,  "
            self.stack_sf = 1
        elif self.value == '"' and self.stack_sf == 1:
            self.stack_sf = 0
        elif self.stack_sf == 1:                                    # "Hello"   
            self.stack.append(ord(self.value))
        elif self.value in numbers and self.stack_sf == 0:
            # 123   
            self.stack.append(int(self.value))

Tatsächlich enthält der Code in action () viele Bedingungen, von denen jede ausgeführt wird, wenn sich dieser Befehl unter dem Zeiger befindet. Alles beginnt mit der Bedingung "Wenn der aktuelle Befehl = Anführungszeichen und das Flag am Anfang der Zeile stack_sf = 0" ist, wird in diesem Fall das Flag auf 1 gesetzt. Wir haben die Zeile eingegeben.

(Andernfalls) Wenn der aktuelle Befehl = Anführungszeichen und das Flag auf 1 gesetzt ist, bedeutet dies, dass das Anführungszeichen ein zweites Mal gefunden wurde und Sie die Eingabe der Zeichenfolge beenden müssen (das Flag stack_sf wird auf 0 gesenkt). Wir sind aus der Reihe.

(Andernfalls) Wenn die ersten beiden Bedingungen nicht funktionierten und das Flag stack_sf = 1 ist, befinden wir uns „innerhalb der Zeile“ und müssen den Code des aktuellen Symbols zum Stapel hinzufügen. Nicht das Zeichen selbst, sondern sein ASCII-Code.

(Andernfalls) Wenn das aktuelle Zeichen zu den Elementen von Zahlen gehört und das Flag stack_sf = 0 ist, ist dies erstens eine Ziffer und zweitens befinden wir uns nicht innerhalb der Zeile. Wir müssen das aktuelle Zeichen = Ziffer zum Stapel hinzufügen. Fügen Sie nicht den Zeichencode hinzu, sondern das Zeichen selbst. Denken Sie immer noch daran, dass es eine Nummer 1 gibt und sie einen Code = 49 hat. Wenn wir uns also innerhalb der Zeile befinden, müssen wir 49 zum Stapel hinzufügen, und wenn es nur im Programm ist, sollte Befehl 1 zum Stapel 1

hinzugefügt werden. Im Folgenden werden alle Bedingungen elif (andernfalls, wenn ...), also schreibe ich sie einfach "wenn". Darüber hinaus sind alle Bedingungen doppelt. Sie müssen das aktuelle Zeichen auf Gleichheit mit dem Befehl und auf die Tatsache überprüfen, dass wir uns nicht in der Zeichenfolge befinden (innerhalb der Zeichenfolge werden alle Zeichen unterschiedlich verarbeitet). Sie könnten dies alles optimierter schreiben, aber mit dieser Lösung können Sie die Aufmerksamkeit auf diese Stirn lenken.

        elif self.value in operators and self.stack_sf == 0:
            b = self.stack.pop()
            a = self.stack.pop()
            if self.value == '+':
                res = a + b                                         # a+b  
            elif self.value == '-':
                res = a - b                                         # a-b  
            elif self.value == '*':
                res = a * b                                         # a*b  
            elif self.value == '/':
                if b == 0:
                    res = 0
                else:
                    res = a // b                                    # a//b  
            elif self.value == '%':
                res = a % b                                         # a%b  
            self.stack.append(res)

Wenn sich das aktuelle Zeichen unter den Operatoren befindet (und stack_sf = 0), bedeutet dies, dass wir in eine arithmetische Operation geraten sind. Alle sind genau gleich: 1) die Nummer b wird entfernt (mit Löschung), 2) die Nummer a wird entfernt (mit Löschung), 3) res = der Wert der Operation zwischen a und b, 4) res wird auf den Stapel geschoben. Bei der Division durch 0 lautet die Antwort 0, obwohl der Autor der Sprache die Wahl zwischen 0 und 1 getroffen hat.

        elif self.value == '!' and self.stack_sf == 0:
            a = self.stack.pop()
            if a == 0:
                a = 1
            else:
                a = 0
            # 0->1, 1->0
            self.stack.append(a)

        elif self.value == '`' and self.stack_sf == 0:
            a = self.stack.pop()        # 
            b = self.stack.pop()        # 
            if b > a:
                res = 1
            else:
                res = 0
            # b>a -> 1|0
            self.stack.append(res)

Wenn das aktuelle Zeichen "!" Ist, müssen Sie den Kopf (Scheitelpunkt) des Stapels ersetzen: Es war 0 - es wird 1, es gab etwas anderes als 0 - es wird 1. Wenn das aktuelle Zeichen "» "(Apostroph) ist, müssen Sie die Oberseite und den Rücken überprüfen : 1) Wenn der Scheitelpunkt größer als der Scheitelpunkt ist, dann 1, 2) Wenn der Scheitelpunkt kleiner (oder gleich) dem Scheitelpunkt ist, wird 0 auf den Stapel gelegt. Bitte beachten Sie, dass beim Entfernen von Vergleichselementen diese entfernt (gelöscht) werden, nicht werden kopiert.

        elif self.value == '?' and self.stack_sf == 0:
            # ? ( )
            a = random.randint(1, 4)
            self.vector = a

        elif self.value == ':' and self.stack_sf == 0:              #  
            last = self.stack.pop()
            self.stack.append(last)
            self.stack.append(last)

        elif self.value == '\\' and self.stack_sf == 0:             # ab => ba
            a = self.stack.pop()
            b = self.stack.pop()
            self.stack.append(a)
            self.stack.append(b)

Wenn das aktuelle Zeichen "?" Ist, müssen Sie eine zufällige Richtung wählen und diese befolgen. Wir verwenden die Funktion random.randint (1, 4), die die Zahlen 1,2,3,4 generiert und den neuen Wert in point.vector einfügt.

Wenn das aktuelle Zeichen ":" ist, legen wir eine Kopie der Oberseite des Stapels auf den Stapel, d. H. Lesen Sie es und fügen Sie es dann zweimal zum Stapel hinzu.

Wenn das aktuelle Zeichen "\\" (Backslash) ist, müssen Sie den Scheitelpunkt und den Unterscheitelpunkt vertauschen. Wir bekommen zwei Zahlen, legen sie in umgekehrter Reihenfolge auf den Stapel.

        elif self.value == '#' and self.stack_sf == 0:              #  ""
            self.step()

        elif self.value == ',' and self.stack_sf == 0:              # =65=A
            value = self.stack.pop()
            print(chr(value), end='')

        elif self.value == '.' and self.stack_sf == 0:              # Print 
            a = self.stack.pop()
            print(a, end='')

Wenn das aktuelle Symbol "#" (Pfund) ist, müssen Sie über den nächsten Befehl (in Richtung) springen. Beachten Sie, dass am Ende von action () ein bedingungsloser Sprung vorwärts erfolgt. Self.step () ermöglicht es Ihnen, vorwärts zum nächsten Befehl zu gelangen. Nachdem wir self.step () in die "#" - Verarbeitung geschrieben haben, machen wir tatsächlich zwei Sprünge und "überspringen" den nächsten Befehl nach dem "#".

Wenn das aktuelle Zeichen "," (Komma) ist, müssen Sie ein Zeichen drucken, dessen ASCII-Code sich oben auf dem Stapel befindet. Wenn die Nummer 65 vorhanden ist, sollte „A“ angezeigt werden.

Wenn das aktuelle Zeichen "." Ist. (Punkt), dann müssen Sie die Nummer, die oben auf dem Stapel liegt, genau wie eine Nummer drucken. Wenn dort 65 liegt, müssen Sie "65" anzeigen. In beiden Fällen wird der Parameter end = '' während der Ausgabe so eingestellt, dass kein neuer Zeilenumbruch auftritt.

        elif self.value == '_' and self.stack_sf == 0:              #  "_"
            test = self.stack.pop()
            if test == 0:
                #  = 0, (2)
                self.vector = 2
            else:
                #  !=0, (4)
                self.vector = 4

        elif self.value == '|' and self.stack_sf == 0:              #  "|"
            test = self.stack.pop()
            if test == 0:
                self.vector = 3
            else:
                self.vector = 1

Wenn das aktuelle Zeichen "_" (Unterstrich) ist, überprüfen Sie es horizontal. Wir entfernen die zu prüfende Zahl aus dem Stapel (Test), wenn sie = 0 ist, bewegen wir uns nach rechts (Vektor = 2), wenn sie! = 0 ist, bewegen wir uns nach links (Vektor = 4).

Wenn das aktuelle Zeichen = "|" (vertikaler Balken), dann müssen Sie eine vertikale Prüfung durchführen. Wir entfernen die Zahl (Test) vom Stapel, wenn sie = 0 ist, bewegen wir uns nach unten (Vektor = 3), andernfalls bewegen wir uns nach oben (Vektor = 1).

        elif self.value == '$' and self.stack_sf == 0:              #  
            self.stack.pop()

        elif self.value == '~' and self.stack_sf == 0:              # Input: A => 65
            val = input(' : ')
            self.stack.append(ord(val[0]))

        elif self.value == '&' and self.stack_sf == 0:              # Input: 65 => 65
            val = int(input(' : '))
            self.stack.append((val))

        elif self.value == 'p' and self.stack_sf == 0:              # x, y, symcode
            x = self.stack.pop()                                    # A(x,y) = symcode
            y = self.stack.pop()
            symcode = self.stack.pop()
            A[x][y] = chr(symcode)

        # x, y, value=A(x,y)
        elif self.value == 'g' and self.stack_sf == 0:
            x = self.stack.pop()                                    # ord(value) => 
            y = self.stack.pop()
            value = A[x][y]
            self.stack.append(ord(value))

Wenn das aktuelle Zeichen = "$" ist, müssen Sie den Scheitelpunkt entfernen. Mach einfach pop ().

Wenn das aktuelle Zeichen = "~" (Tilde) ist, fragen wir den Benutzer nach einem Zeichen und legen seinen ASCII-Code auf den Stapel. Benutzer "A" (Englisch) gesendet, müssen wir 65 auf den Stapel setzen. Nur für den Fall, wir werden val [0] setzen, sonst kann der Benutzer "Apple" eingeben und es in Code übersetzen funktioniert nicht.

Wenn das aktuelle Zeichen = "&" (kaufmännisches Und) ist, fragen wir den Benutzer nach einer Nummer und legen die Nummer auf den Stapel. Sie haben 65 eingegeben, Sie müssen 65 auf den Stapel legen.

Jetzt die beiden schwierigsten Teams.

Wenn das aktuelle Zeichen = "p" ist, müssen Sie die Zellkoordinaten und den ASCII-Code des Zeichens aus dem Stapel extrahieren und dieses Zeichen dann in diese Koordinaten in Feld A einfügen. Angenommen, 1.2.65 war auf dem Stapel und wir haben (1.2) und 65, wir sollten das Symbol "A" in Zelle (1.2) setzen. Ich stelle noch einmal fest: Wir haben drei Zahlen und setzen ein Symbol in die Koordinaten.

Wenn das aktuelle Zeichen = "g" ist, werden die Zellkoordinaten vom Stapel abgerufen, die Zelle wird auf dem Feld gesucht, das Zeichen wird von dort übernommen und sein ASCII-Code wird auf den Stapel geschoben. Angenommen, das Symbol „B“ lag in Zelle (2,3) auf dem Feld, das aktuelle Team erhielt „g“ und wir erhielten 2,3 vom Stapel. In diesem Fall gehen wir die Koordinaten (2,3) entlang, erhalten von dort das Symbol „B“, übersetzen es in die Zahl 66 (Symbolcode B) und legen 66 auf den Stapel.

        elif self.value == '>' and self.stack_sf == 0:              # >
            self.vector = 2
        elif self.value == '<' and self.stack_sf == 0:              # <
            self.vector = 4
        elif self.value == '^' and self.stack_sf == 0:              # ^
            self.vector = 1
        elif self.value == 'v' and self.stack_sf == 0:              # v
            self.vector = 3
        self.step()                                                 #  

Nun, und die letzten Codezeilen: die Organisation des Bewegens des Zeigers über das Feld. Hier ist alles einfach: Wir schauen auf das Symbol und ändern die Richtung (Vektor). Ganz am Ende der action () -Funktion steht self.step (), das einen Schritt in die aktuelle Richtung macht. Somit ist action () sowohl die Ausführung der Aktion als auch der Schritt für das nächste Zeichen.

Fazit


Das Schreiben dieses Compilers war eine Freude. Und wie viel Freude brachte die Momente, in denen Sie einen bestimmten Code in das Programm werfen und dieser ausgeführt wird (richtig!) Und Sie beobachten ihn! Die Seite befungius.aurlien.net hat bei der Arbeit sehr geholfen , an der der Autor den Befunge Online-Interpreter (in JavaScript) gepostet hat. Hier ist sein Video von einer Konferenz www.youtube.com/watch?v=oCPT3L33848 , in der er über die Sprache spricht.

Zum Testen können Sie fast jedes Programm auf Befunge verwenden, mit Ausnahme derjenigen, die ein Feld als Torus benötigen, d. H. Übergänge in verschiedene Richtungen der Welt haben. Bei einigen Programmen muss das Feld selbst angezeigt werden (z. B. ändert das Zählerprogramm die Koordinate A [0] [1]). Fügen Sie also entweder die Ausgabe dieser Koordinate auf dem Bildschirm ein oder zeigen Sie nach jedem Schritt die gesamte Matrix A an.

In jedem Fall ist dies kein Programm, das zum Leuchten gebracht wurde, sondern ein Trainingscode, der hinzugefügt und bearbeitet werden kann. Tippfehler sind möglich, obwohl ich sie schon oft getestet habe. Kritik ist nicht erwünscht, aber nicht verboten. Viel Glück an alle und guten Code.

Hallo Welt!

0"!dlrow olleH">:#,_@

Bazz

0> 1+:3%v
>^  v%5:_:5% v
,v.:_v     v0_0"zzub"v
"v         #
     >0"zzub"v
"   v"fizz"<         <
^<         $<>:#,_v
    >      #^^#   <

Fibonacci

62*1+v>01p001>+v>\:02p\:02gv
     0       ^             <
     .         :p
     "         .1
        v 0," "<0
     "  >1g12-+:|
     ,          @
     >^

Anzahl

>91+:9`v
p   v  _v
    >$0 v
^ 01+*68<

Aber das funktioniert nicht, wenn man das Feld verlässt

0>:00p58*`#@_0>:01p78vv$$<
@^+1g00,+55_v# !`\+*9<>4v$
@v30p20"?~^"< ^+1g10,+*8<$
@>p0\>\::*::882**02g*0v >^
`*:*" d":+*:-*"[Z"+g3 < |<
v-*"[Z"+g30*g20**288\--\<#
>2**5#>8*:*/00g"P"*58*:*v^
v*288 p20/**288:+*"[Z"+-<:
>*%03 p58*:*/01g"3"* v>::^
   \_^#!:-1\+-*2*:*85<^

Links und zusätzliches Material:

  1. Vollständiger Code
  2. Online Version
  3. Sie ist in JavaScript
  4. Dokumentation (Englisch)
  5. Wiki-Artikel (Englisch)

All Articles