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.
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.
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.
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
int
float
bool
string
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
Funktionen können mit der folgenden Syntax definiert werden.
fn add(a: int, b: int) -> int {
return a + b;
}
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...
Als nächstes wollen wir sehen, wie dieses Programm funktioniert.
Erzeugt ein Fenster (entsprechender Bildschirm) im Vorschaubildschirm. Dies kann auf die gleiche Weise wie die Verarbeitung erfolgen.
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 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.
Die Möglichkeit, in die Konsole einzutreten, wurde noch nicht implementiert. Es tut uns leid.
Ä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ß.
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.
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];
}
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.
size()
background()
fill()
stroke()
strokeWeight()
textSize()
textAlign()
text()
circle()
rect()
ellipse()
println()
print()
typeof ()
(Variablentyp prüfen)millis()
wait ()
(wait (ms) => ms beendet die Verarbeitung für Millisekunden)random()
min ()
(gibt den kleinsten Wert unter mehreren Argumenten zurück)max ()
(gibt den Maximalwert unter mehreren Argumenten zurück)range ()
(gibt ein Array von Ganzzahlwerten im angegebenen Bereich zurück)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.
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.
Die eingegebene Zeichenfolge wird zeilenweise in einem Objekt vom Typ "ArrayList
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.
(
für )
und }
für {
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.
Die Konsole speichert auch Zeichenfolgen in Objekten vom Typ "ArrayList
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 ()".
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.