In früheren Artikeln Vergleich und logische Operatoren und Unterstützt if-Anweisungen ) Hat. Ich möchte die while-Anweisung als Erweiterung davon unterstützen.
Stellen Sie sicher, dass Sie es einfach machen möchten. Zum Beispiel, wenn Sie ein Programm wie das folgende haben Wiederholen Sie dies mit der while-Anweisung, überspringen Sie die Wiederholung, wenn "v" zu "2" wird, und versuchen Sie, "2" mit "println ()" auszugeben.
v = 0
while (v < 4) {
v = v + 1
if (v == 2) {
break
}
}
println(v)
Wie zu implementieren Die Implementierung der while-Anweisung entspricht fast der Korrespondenz der if-Anweisung. Die Implementierung der break-Anweisung entspricht fast der Unterstützung für Funktionsrückgabewerte. Eine ausführliche Erklärung finden Sie hier. Lassen Sie uns diese sofort implementieren.
Fahren Sie mit der Implementierung fort. Informationen zu Syntaxparser (Parser) und Interpreter (Interpreter) Schauen wir uns die Änderungen und Ergänzungen der Reihe nach an.
Parser.java
Eine Implementierung von Parser.java. Fügen Sie eine Definition hinzu, wie es für die Bedeutung des Tokens funktioniert. Da "while" und "break" reservierte Wörter sind, habe ich sie zu "<-Update" hinzugefügt.
Parser.java
public Parser() {
degrees = new HashMap<>();
degrees.put("(", 80);
degrees.put("*", 60);
degrees.put("/", 60);
degrees.put("+", 50);
degrees.put("-", 50);
degrees.put("==", 40);
degrees.put("!=", 40);
degrees.put("<", 40);
degrees.put("<=", 40);
degrees.put(">", 40);
degrees.put(">=", 40);
degrees.put("&&", 30);
degrees.put("||", 30);
degrees.put("=", 10);
factorKinds = Arrays.asList(new String[] { "digit", "ident" });
binaryKinds = Arrays.asList(new String[] { "sign" });
rightAssocs = Arrays.asList(new String[] { "=" });
unaryOperators = Arrays.asList(new String[] { "+", "-", "!" });
reserved = Arrays.asList(new String[] { "function", "return", "if", "else", "while", "break"}); // <-- Update
}
Es ist eine Änderung des zu analysierenden Teils.
Es wurde ein Aufruf für die Funktion hinzugefügt, die while
zum ersten <-Add
analysiert.
Break
Analyse zum zweiten <-Add
hinzugefügt.
Die Analyse von "break" erfolgt durch Zuweisen zu "token.kind", wobei die Bedeutung des Tokens für "brk" bestimmt wird.
Parser.java
private Token lead(Token token) throws Exception {
if (token.kind.equals("ident") && token.value.equals("function")) {
return func(token);
} else if (token.kind.equals("ident") && token.value.equals("return")) {
token.kind = "ret";
if (!token().kind.equals("eob")) {
token.left = expression(0);
}
return token;
} else if (token.kind.equals("ident") && token.value.equals("if")) {
return if_(token);
} else if (token.kind.equals("ident") && token.value.equals("while")) { // <-- Add
return while_(token);
} else if (token.kind.equals("ident") && token.value.equals("break")) { // <-- Add
token.kind = "brk";
return token;
} else if (factorKinds.contains(token.kind)) {
return token;
} else if (unaryOperators.contains(token.value)) {
token.kind = "unary";
token.left = expression(70);
return token;
} else if (token.kind.equals("paren") && token.value.equals("(")) {
Token expr = expression(0);
consume(")");
return expr;
} else {
throw new Exception("The token cannot place there.");
}
}
Es ist eine Änderung des zu analysierenden Teils.
Es wurde eine Methode while_ ()
hinzugefügt, die while-Anweisungen analysiert.
Wie oben erwähnt, ist die Analyse der if-Anweisung eine einfache Erklärung.
Das Analyseergebnis der while-Anweisung wird im Argument-Token zusammengefasst.
Die Verarbeitung in der Methode "while_ ()" dient dazu, die Token in der while-Anweisungsdefinition der Reihe nach zu verfolgen.
Erstens bestimmt die Zuweisung zu "token.kind" die Bedeutung des Tokens zu "while".
Dann verbraucht es das (
am Anfang der bedingten Anweisung.
Die Methode expression ()
analysiert die bedingte Anweisung der while-Anweisung und hält sie in token.left
.
Dann verbraucht es das Ende )
der bedingten Anweisung.
Analyse von Verarbeitungsblöcken.
Wenn sich am Anfang des Blocks ein {
befindet, ist es von einem {
Token und einem }
Token umgeben.
Rufen Sie die body ()
Methode auf, die den Verarbeitungsblock analysiert, und halten Sie ihn in token.block
.
Wenn am Anfang des Blocks kein "{" steht, wird davon ausgegangen, dass es nur einen Verarbeitungsblock gibt, der in "token.block" gespeichert ist.
Parser.java
private Token while_(Token token) throws Exception {
token.kind = "while";
consume("(");
token.left = expression(0);
consume(")");
if (token().value.equals("{")) {
token.block = body();
} else {
token.block = new ArrayList<Token>();
token.block.add(expression(0));
}
return token;
}
Interpreter.java
Eine Implementierung von Interpreter.java.
Dies ist eine Änderung der Methode body ()
, die Ausdrücke nacheinander ausführt.
Boolean [] brk
zur Signatur von body ()
hinzugefügt.
brk
hat zwei Rollen.
Das erste ist, dass, wenn "brk" nicht "null" ist, "break" möglich ist.
Das zweite ist, dass wenn es "break" war, es auch an den Anrufer weitergegeben wird.
Der Grund, brk
zu einem Array-Typ von boolean
zu machen, besteht darin, einen Wert an den Aufrufer zurückzugeben.
Bei "// <-Add 2" wird bestimmt, ob das nacheinander auszuführende Token "break" ist. Wenn der Token "break" ist, bestimmen Sie, ob es möglich ist, "break" zu machen. Ersetzen Sie "true", um dem Anrufer mitzuteilen, dass es "break" für "brk" war.
Es wurde ein Methodenaufruf zur Verarbeitung von while-Anweisungen zu // <-Add 1
hinzugefügt.
Wenn also "return" im Verarbeitungsblock der while-Anweisung aufgerufen wird, kann es zur oberen Ebene zurückkehren.
Bestimmt, ob "ret [0]" "true" ist, und bricht die "body ()" - Methode ab.
Interpreter.java
public Object body(List<Token> body, boolean[] ret, boolean[] brk) throws Exception {
for (Token exprs : body) {
if (exprs.kind.equals("if")) {
Object val = if_(exprs, ret, brk);
if (ret != null && ret[0]) {
return val;
}
} else if (exprs.kind.equals("ret")) {
if (ret == null) {
throw new Exception("Can not return");
}
ret[0] = true;
if (exprs.left == null) {
return null;
} else {
return expression(exprs.left);
}
} else if (exprs.kind.equals("while")) { // <-- Add 1
Object val = while_(exprs, ret);
if (ret != null && ret[0]) {
return val;
}
} else if (exprs.kind.equals("brk")) { // <-- Add 2
if (brk == null) {
throw new Exception("Can not break");
}
brk[0] = true;
return null;
} else {
expression(exprs);
}
}
return null;
}
Eine while_ ()
Methode, die eine while-Anweisung ausführt.
token.left
enthält den bedingten Ausdruck.
Die Methode isTrue ()
bestimmt, ob der bedingte Ausdruck wahr ist.
Wiederholen Sie token.block
, während der bedingte Ausdruck wahr ist.
Selbst wenn der bedingte Ausdruck wahr ist, im Methodenaufruf body ()
Wenn es sich um ein "Return" - oder "Break" -Token handelt, wird die Iteration unterbrochen.
Interpreter.java
public Object while_(Token token, boolean[] ret) throws Exception {
boolean[] brk = new boolean[1];
Object val;
while (isTrue(token.left)) {
val = body(token.block, ret, brk);
if (ret != null && ret[0]) {
return val;
}
if (brk[0]) {
return null;
}
}
return null;
}
Als nächstes folgt die Antwort auf das Ändern der Signatur der Methode "body ()".
Break
kann auch in der if-Anweisung aufgerufen werden.
Fügen Sie daher der Signatur der Methode if_ () ein formales Argument brk hinzu.
Übergeben Sie es im Aufruf an die body ()
Methode.
Interpreter.java
public Object if_(Token token, boolean[] ret, boolean[] brk) throws Exception {
List<Token> block;
if (isTrue(token.left)) {
block = token.block;
} else {
block = token.blockOfElse;
}
if (block != null) {
return body(block, ret, brk);
} else {
return null;
}
}
Dies ist eine Antwort auf die Änderung der Signatur der body ()
Methode.
Beim Aufruf der body ()
Methode in <-Update
Weil es sich um einen Prozess in einem Kontext handelt, in dem der Aufruf "break" nicht möglich ist
Das Argument brk
der body ()
Methode wird mit null
aufgerufen.
Interpreter.java
public Map<String, Variable> run() throws Exception {
body(body, null, null); // <-- Update
return variables;
}
public static class DynamicFunc extends Func {
public Interpreter context;
public List<Token> params;
public List<Token> block;
@Override
public Object invoke(List<Object> args) throws Exception {
for (int i = 0; i < params.size(); ++i) {
Token param = params.get(i);
Variable v = context.variable(context.ident(param));
if (i < args.size()) {
v.value = context.value(args.get(i));
} else {
v.value = null;
}
}
boolean[] ret = new boolean[1];
return context.body(block, ret, null); // <-- Update
}
}
Das folgende Programm verwendet die obige Implementierung
v = 0
while (v < 4) {
v = v + 1
if (v == 2) {
break
}
}
println(v)
Drucken von "2" nacheinander auf die Standardausgabe.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "v = 0";
text += "while (v < 4) {";
text += " v = v + 1";
text += " if (v == 2) {";
text += " break";
text += " }";
text += "}";
text += "println(v)";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 2
}
Das ist alles für die Implementierung. Vielen Dank.
Die vollständige Quelle finden Sie hier.
Calc https://github.com/quwahara/Calc/tree/article-12-while-r2/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht dem Umfang ** http://qiita.com/quwahara/items/d9f932195da1b655b617
Recommended Posts