[JAVA] Verwenden Sie Processing, um eine Ausführungsumgebung für Ihre eigene Live-Codierungssprache mit einem Vorschaubildschirm der Editor-Konsole zu implementieren

Einführung

Dies ist diejenige, die am 17. September 2020 in der EP-Praxis der FMS-Abteilung der Meiji-Universität angekündigt wurde. Nun, das interessiert mich nicht, also schauen Sie bitte. Ich habe den Quellcode auf github gestellt.

Überblick

Das Programm selbst wird in einer Sprache namens Processing implementiert, die ein Cover von Java zu sein scheint. Der Ausführungsbildschirm besteht aus einem Texteditor, einer Konsole und einem Vorschaubildschirm. D4A19692-CDAF-4B48-B178-A0C1720EA227.jpeg

Sprachgrammatik

Grundsätzlich kann es in der gleichen Syntax wie Java geschrieben werden. Es gibt einige Teile, die sich von Java unterscheiden, daher werde ich ein wenig erklären.

Variable Aussage

Die Variablendeklaration hat die folgende Syntax.

let a = 10;
println(a); // -> 10
println(typeof(a)); // -> int

let b: float = 10;
println(typeof(b)); // -> float

Derzeit verfügbare integrierte Typen

4 Typen und ihre Anordnungen. Wenn Sie Lust dazu haben, möchte ich es bald erhöhen. Die Array-Deklaration kann wie folgt geschrieben werden.

let arr1 = {0, 1, 2};
let arr2 = {{0, 1}, {1, 2}};
let arr3 = int[2][2]; // make {{0, 0}, {0, 0}}

Es gibt auch Rust in einer Sprache mit einer ähnlichen Grammatik, aber im Fall von Rust wird es zu einer Konstanten, wenn Sie es mit let deklarieren, und wenn Sie mut hinzufügen, wird es zu einer Variablen, während in dieser Sprache let Die Variable wird mit deklariert. Ich habe noch keine konstante Deklaration abgegeben, aber ich denke darüber nach, daraus eine const -Anweisung wie JavaScript zu machen.

Sie können auch Folgendes schreiben.

let c, d = 10, 20;
println(c, d); // -> 10 20
c, d = d, c; // swap
println(c, d); // -> 20 10

Funktionsdefinition

Funktionen können mit der folgenden Syntax definiert werden.

fn add(a: int, b: int) -> int {
    return a + b;
}

Ergänzung

Da es durch viel Eilarbeit gemacht wird, gibt es viele integrierte Funktionen, die im Vergleich zu anderen Sprachen nicht unterstützt werden können, und es gibt viele Probleme, wie zum Beispiel, dass die Objektorientierung überhaupt nicht unterstützt werden kann. bitte verzeih mir...

wie benutzt man

Als nächstes wollen wir sehen, wie dieses Programm funktioniert.

Fenstergenerierung

Erzeugt ein Fenster (entsprechender Bildschirm) im Vorschaubildschirm. Dies kann auf die gleiche Weise wie die Verarbeitung erfolgen. 369C08DD-1B9F-4309-B204-637C42AAEEE9.jpeg

In diesem Beispiel wird auf dem Vorschaubildschirm der Bildschirm mit dem Verhältnis 800x600 angezeigt. Sie können die Hintergrundfarbe auch mit der Funktion background () angeben, genau wie bei der Verarbeitung.

Verwenden der Konsole

Verwenden Sie die Funktion println (), um eine Zeichenfolge auf der Konsole zu drucken. Wenn Sie am Ende der Zeile keinen Zeilenumbruch drucken möchten, können Sie auch die Funktion print () verwenden. F741BD52-53ED-4A46-BC77-8EB8A0C5A810.jpeg

Die Möglichkeit, in die Konsole einzutreten, wurde noch nicht implementiert. Es tut uns leid.

Verarbeitung für jeden Frame

Ähnlich wie bei der Verarbeitung können Sie die Frame-für-Frame-Verarbeitung schreiben, indem Sie die Funktion draw () definieren. Das folgende Beispiel implementiert das Verhalten des allmählichen Änderns der Hintergrundfarbe von Schwarz nach Weiß. B1C3DE40-D435-4DBB-B7AC-779F4C83A61B.jpeg C21F3A6A-0BD1-4D9A-93F4-3C7D743AD2EE.jpeg 6CA58A85-3ECC-4D18-B0F5-1C33075FC954.jpeg

Verarbeitung für die Maus

Die Funktion zum Aufrufen beim Klicken mit der Maus wurde implementiert, wenn die Funktionen mouseX, mouseY und mousePressed () definiert sind, die die Mauskoordinaten angeben. Sie können also den folgenden Prozess schreiben, um das angeklickte Teil und seine oberen, unteren, linken und rechten Quadrate zu invertieren. 083D3856-E7E3-4756-A4E1-2C5EFAABF1A8.jpeg C1B173B7-AF1C-4601-AA90-37E710CF0522.jpeg 3AE8D23F-CBA3-49ED-86BF-BA14C93DE979.jpeg

Der Code ist nicht vorrätig, daher werde ich ihn separat veröffentlichen.

size(500, 500);

let board = int[5][5];
let count = 0;

board[0][2] = 1;
board[1][1] = 1;
board[1][2] = 1;
board[2][3] = 1;
board[2][4] = 1;
board[3][3] = 1;

fn draw() -> void {
    background(0);
    fill(255, 255, 0);
    count = 0;
    for(let i = 0; i < 5; i++) {
        for(let j = 0; j < 5; j++) {
            if(board[i][j] == 1) {
                rect(100 * i, 100 * j, 100, 100);
            }
            else {
                count++;
            }
        }
    }
    if(count == 25) {
        textSize(32);
        textAlign(CENTER);
        text("CLEAR!", width / 2, height / 2);
    }
}

fn mousePressed() -> void {
    let x = mouseX / 100;
    let y  = mouseY / 100;
    for(let i = max(0, x - 1); i <= min(4, x + 1); i++) {
        board[i][y] = 1 - board[i][y];
    }
    for(let i = max(0, y - 1); i <= min(4, y + 1); i++) {
        board[x][i] = 1 - board[x][i];
    }
    board[x][y] = 1 - board[x][y];
}

Integrierte Funktionen, die verwendet werden können

Im obigen Programm sind einige Funktionen erschienen, mit denen Sie in der Verarbeitung vertraut sind. Einige der in Processing verfügbaren Funktionen wurden auch für die Verwendung in dieser Homebrew-Sprache implementiert. Derzeit verfügbare integrierte Funktionen sind alle folgenden, mit oder ohne Verarbeitung.

Interne Implementierung

Fahren wir nun mit der detaillierten internen Implementierung fort. Ab diesem Zeitpunkt kann es für diejenigen, die nicht über ausreichende Verarbeitungskenntnisse verfügen oder nicht über viel Programmiererfahrung verfügen, etwas schwierig sein.

Editor-Implementierung

Zeichen eingeben

Es wird implementiert, indem die Funktionen keyPressed, keyPressed (), keyTyped () usw. vollständig genutzt werden. Schauen wir uns zunächst den folgenden Code in der Funktion draw () an.

if(keyPressed) {
    if(prevKey.equals(String.valueOf(key))) {
        if(keyt < dur) keyt++;
        else {
            keyt = dur / 2;
            if(key != CODED) keyTyped();
        }
    }
    else {
        keyt = 0;
        if(key != CODED) keyTyped();
    }
}
else {
    keyt = 0;
    prevKey = "";
}

prevKey ist eine Variable, die den zuvor eingegebenen Schlüssel enthält. keyt gibt an, wie viele Frames seit der Eingabe des Schlüssels vergangen sind. dur ist der Wert, der als Schwellenwert für keyt festgelegt wurde. Wenn keyt den Schwellenwert dur überschreitet, während dieselbe Taste wie beim letzten Mal eingegeben wird, ersetzen Sie durt und key durch dur / 2 Wenn es sich nicht um einen codierten Schlüssel wie oder Control handelt, wird die Funktion keyTyped () aufgerufen. Wenn ein anderer als der vorherige Schlüssel eingegeben wird, wird "keyt" nicht aktiviert und "0" wird "keyt" zugewiesen. Außerdem hat die Funktion keyPressed () die folgende Implementierung.

void keyPressed() {
    if(key == CODED) {
        if(keyCode == LEFT) {
            cursor.prev();
            beforeCursor.prev();
        }
        else if(keyCode == RIGHT) {
            cursor.next();
            beforeCursor.next();
        }
        else if(keyCode == UP) {
            cursor.up();
            beforeCursor.up();
        }
        else if(keyCode == DOWN) {
            cursor.down();
            beforeCursor.down();
        }
        else {
            keyFlags.put(keyCode, true);
        }
    }
}

cursor, beforeCursor sind Objekte vom Typ Cursor und haben Methoden line, row zur Darstellung von Zeilen und Spalten. Cursor repräsentiert die aktuelle Cursorposition. beforeCursor wird verwendet, um die Bereichsauswahl zu implementieren, daher werde ich es später erklären. Außerdem ist keyFlags ein Objekt vom Typ HashMap <Integer, Boolean> und gibt an, ob keyCode angibt, dass ein anderer codierter Schlüssel als der Kreuzschlüssel eingegeben wurde. Diese keyPressed () Funktion bewegt den Cursor, wenn die Kreuztaste gedrückt wird, und ändert keyFlags, wenn eine andere codierte Taste eingegeben wird. .. Um die keyFlags zu ändern, wenn der Schlüssel, dessen keyFlags true ist, freigegeben wird, wird die Funktion keyReleased () wie folgt definiert.

void keyReleased() {
    if(key == CODED) {
        if(keyFlags.get(keyCode) != null) {
            keyFlags.put(keyCode, false);
        }
    }
    keyPressed = false;
}

Das Ändern von keyPressed in false sollte nicht notwendig sein, aber wenn Sie es aus irgendeinem Grund nicht schreiben, wird das Verhalten von keyPressed seltsam sein, also werde ich es schreiben.

Die Funktion keyTyped () beschreibt das Verhalten, das jedem eingegebenen Schlüssel entspricht. Wenn Sie es hier schreiben, wird es lange dauern. Wenn Sie es sehen möchten, können Sie es unter Quellcode-Link überprüfen.

Anzeige der Zeichenkette

Die eingegebene Zeichenfolge wird zeilenweise in einem Objekt vom Typ "ArrayList " gespeichert. Es zeigt sie nur der Reihe nach an. Wenn die Anzahl der Zeilen den Anzeigebereich des Editors überschreitet, wird die Bildlaufleiste auf der rechten Seite des Editors angezeigt. Die Höhe, in der die Zeichenfolge ausgegeben wird, ändert sich entsprechend der Position der Bildlaufleiste.

Funktionsimplementierung

Um sich selbst als Editor zu bezeichnen, wenn Sie nur Zeichen eingeben können, unterscheidet es sich nicht von Notepad, sodass Sie einige Funktionen benötigen. Die Funktionen des aktuell implementierten Editors sind wie folgt.

Bei der Implementierung der Bereichsauswahl ist einer von "beforeCursor" und "cursor" der Anfang des Bereichs und der andere das Ende des Bereichs. Der Rest ist in der Funktion keyTyped () durcheinander. Wenn Sie also interessiert sind, schauen Sie sich das an.

Konsolenimplementierung

Die Konsole speichert auch Zeichenfolgen in Objekten vom Typ "ArrayList ". Die Anzeige entspricht der des Editors.

Implementierung eines Sprachverarbeitungssystems

Die diesmal implementierte Sprache ist eine einfache dynamisch typisierte Dolmetschersprache. Ich mag jedoch keine dynamisch typisierten Sprachen, daher sieht der Code wie eine statisch typisierte Sprache aus, die Typinferenz durchführt. Der Ablauf der Verarbeitung ist etwas speziell, und die ursprüngliche Phrasenanalyse und Syntaxanalyse werden durchgeführt, und der abstrakte Syntaxbaum wird interpretiert und ausgeführt, aber die diesmal implementierte ist eine grobe Implementierung, bei der verschiedene Dinge weggelassen werden. Ich bin. Ziehen Sie beispielsweise in Betracht, den folgenden Code auszuführen.

let a = 10;

Zu diesem Zeitpunkt erzeugt die Klasse "Lexer" das folgende Berechnungsergebnis.

[
    Token("let", "let"),
    Token("id", "a"),
    Token("expr", "expr"),
    Token("int", "10"),
    Token("endExpr", "endExpr"),
    Token("endLet", "endLet")
]

Dies ist ein Objekt vom Typ ArrayList <Token>. Ein Objekt vom Typ "Token" hat zwei Felder, "Art" und "Wert". Token (a, b) wird als Token mit einem Wert von kind von a und einem Wert von value von b geschrieben. Stellen Sie sich vor, Sie repräsentieren ein Typobjekt. Dieses Array wird dann als Befehlssequenz interpretiert und ausgeführt. Die Ausführung der let -Anweisung erfolgt mit dem folgenden Code.

    .
    .
    .
else if(res.get(i).kind.equals("let")) {
    ArrayList<String> vars = new ArrayList<String>();
    ArrayList<String> values = new ArrayList<String>();
    i++;
    String type = "", exprType = "";
    if(res.get(i).kind.equals("type")) {
        type = res.get(i).value;
        i++;
    }
    while(!res.get(i).kind.equals("endLet")) {
        if(res.get(i).kind.equals("id")) {
            vars.add(res.get(i).value);
            varNames.add(loc + "$" + res.get(i).value);
        }
        else if(res.get(i).kind.equals("expr")) {
            ArrayList<Token> expr = new ArrayList<Token>();
            i++;
            int exprNum = 0;
            while(exprNum > 0 || !res.get(i).kind.equals("endExpr")) {
                if(res.get(i).kind.equals("expr")) exprNum++;
                if(res.get(i).kind.equals("endExpr")) exprNum--;
                expr.add(res.get(i));
                i++;
            }
            exprType = calc(expr, loc);
            values.add(expr.get(0).value);
        }
        i++;
    }
    int vs = values.size();
    for(int j = 0; j < vars.size(); j++) {
        if(j < vs) {
            if(type.isEmpty()) {
                variables.put(loc + "$" + vars.get(j), new Variable(exprType, vars.get(j), values.get(j)));
            }
            else {
                variables.put(loc + "$" + vars.get(j), new Variable(type, vars.get(j), values.get(j)));
            }
        }
        else {
            if(type.isEmpty()) {
                variables.put(loc + "$" + vars.get(j), new Variable(exprType, vars.get(j), values.get(vs - 1)));
            }
            else {
                variables.put(loc + "$" + vars.get(j), new Variable(type, vars.get(j), values.get(vs - 1)));
            }
        }
    }
}

res ist eine Variable vom Typ ArrayList <Token>, die das Berechnungsergebnis von Lexer empfängt. Variablen ist eine Variable vom Typ HashMap <String, Variable>, die Variablen speichert. varNames ist eine Variable vom Typ ArrayList <String>, die die Namen von Variablen unter Berücksichtigung des Gültigkeitsbereichs speichert. Die Verwaltung des variablen Bereichs verwendet $. Insbesondere lautet der Gültigkeitsbereichsname der Variablen "a" im globalen Bereich "main $ a" und die in der Funktion "func ()" im globalen Bereich definierte Variable. Der Name, der den Umfang von "a" berücksichtigt, ist "main $ func $ a". Da expr bis endExpr einen Ausdruck darstellen, übergeben Sie die Spalte Token von expr bis endExpr an die Funktion calc (). Auf diese Weise können Sie die Formel berechnen. Auf diese Weise können Sie die Anweisung let ausführen.

Für andere Anweisungen wird die Spalte "Token" auf die gleiche Weise generiert und ausgeführt. Die detaillierte Implementierung der einzelnen Anweisungen finden Sie in der Implementierung der Funktion "Anweisung ()".

Das Ende

Es gibt viele andere Teile, die über die Implementierung erklärt werden können, aber Frische ist das Leben der Geschichte. Deshalb möchte ich die Erklärung hier beenden, weil ich einen Artikel an dem Tag veröffentlichen möchte, an dem diese Arbeit angekündigt wird. Wenn Sie Anfragen, Fragen, Vorschläge, Vorschläge, Verspottungen, Lächerlichkeiten usw. haben, die wir hinzufügen sollen, kommentieren Sie diesen Artikel oder antworten Sie direkt auf Mein Twitter-Konto. Oder senden Sie DM.

Wir entschuldigen uns zutiefst für die verschiedenen Implementierungen, die verschiedenen Erklärungen und die verschiedenen Endungen. Es tut uns leid.

Weitere Informationen zur Implementierung des Sprachverarbeitungssystems finden Sie in diesem Artikel.

Recommended Posts

Verwenden Sie Processing, um eine Ausführungsumgebung für Ihre eigene Live-Codierungssprache mit einem Vorschaubildschirm der Editor-Konsole zu implementieren
[Ruby on Rails 5] Verwenden Sie WorkMail, um eine E-Mail-Adresse für Ihre eigene Domain festzulegen. [Arbeitspost]