Entspricht dem Rückgabewert der Funktion im vorherigen Artikel. Als nächstes möchte ich if-Aussagen unterstützen.
Stellen Sie sicher, dass Sie es einfach machen möchten.
Wenn Sie beispielsweise ein Programm wie das folgende haben, gibt das erste println ()
3
aus.
Das nächste "println ()" zielt darauf ab, "4" zu drucken.
Der Einfachheit halber wird die Authentizität der if-Anweisung bestimmt, indem angenommen wird, dass Nicht-Null wahr und 0 falsch ist.
function f(a) {
if (a) {
return 3
} else {
return 4
}
}
println(f(1))
println(f(0))
Wir werden überlegen, wie es in der Reihenfolge Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann. Die Phrasenanalyse (Lexer) wird nicht geändert.
Informationen zum Implementieren der Syntaxanalyse der if-Anweisung finden Sie unter Syntaxanalyse der Funktionsdefinitionsanweisung, die im vorherigen Artikel implementiert wurde. % 87% E8% A7% A3% E6% 9E% 90 Parser% E3% 81% AE% E5% AE% 9F% E8% A3% 85% E3% 81% AE% E4% BB% 95% E6% 96% B9 ) Ist ähnlich. Da am Anfang ein Schlüsselwort "if" steht und auch die Reihenfolge des Auftretens der darauf folgenden Token festgelegt wird, werden die Token nacheinander verfolgt und analysiert.
Im Interpreter wird die Methode body ()
, die Ausdrücke nacheinander ausführt,
Ändern Sie, um if-Anweisungen zu behandeln.
Dies liegt daran, dass es einfach zu handhaben ist, wenn die if-Anweisung ein "return" enthält.
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 "if" und "else" reservierte Wörter sind, habe ich sie dort hinzugefügt, wo "<-Update" steht.
Parser.java
public Parser() {
degrees = new HashMap<>();
degrees.put("(", 80);
degrees.put("*", 60);
degrees.put("/", 60);
degrees.put("+", 50);
degrees.put("-", 50);
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" }); // <-- Update
}
Es ist eine Änderung des zu analysierenden Teils. Es wurde ein Aufruf für die Funktion hinzugefügt, die "if" analysiert, wobei "<-Add" ist.
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")) { // <-- Add
return if_(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.
Methode if_ ()
hinzugefügt, um if-Anweisungen zu analysieren.
Das Analyseergebnis der if-Anweisung wird im Argument-Token zusammengefasst.
Zusammenfassend fügen wir der Token-Klasse Feldvariablen hinzu.
Die hinzugefügte Feldvariable ist "blockOfElse".
Hinzugefügt, um den Verarbeitungsblock auf der Seite "else" der if-Anweisung zu halten.
Der Typ ist List \ <Token >.
Die Verarbeitung in der if_ ()
-Methode verfolgt die Token in der if-Anweisungsdefinition der Reihe nach.
Erstens bestimmt die Zuweisung zu "token.kind" die Bedeutung des Tokens zu "if".
Dann verbraucht es das (
am Anfang der bedingten Anweisung.
Die Methode expression ()
analysiert die bedingte Anweisung der if-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.
Die Analyse des Verarbeitungsblocks auf der anderen Seite ist fast dieselbe.
Parser.java
private Token if_(Token token) throws Exception {
token.kind = "if";
consume("(");
token.left = expression(0);
consume(")");
if (token().value.equals("{")) {
token.block = body();
} else {
token.block = new ArrayList<Token>();
token.block.add(expression(0));
}
if (token().value.equals("else")) {
consume("else");
if (token().value.equals("{")) {
token.blockOfElse = body();
} else {
token.blockOfElse = new ArrayList<Token>();
token.blockOfElse.add(expression(0));
}
}
return token;
}
Analysiert den Verarbeitungsblock, der vom Token "{" und vom Token "}" eingeschlossen ist.
Auch dort, wo der Verarbeitungsblock der Methode func ()
analysiert wird
Ich habe das Gleiche getan, also habe ich es geändert, um diese Methode aufzurufen.
Parser.java
private List<Token> body() throws Exception {
consume("{");
List<Token> block = block();
consume("}");
return block;
}
Interpreter.java
Eine Implementierung von Interpreter.java.
Dies ist eine Änderung der Methode body ()
, die Ausdrücke nacheinander ausführt.
Es wurde ein Methodenaufruf zur Verarbeitung der if-Anweisung zu // <-Add
hinzugefügt.
Wenn also "return" im Verarbeitungsblock der if-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) throws Exception {
for (Token exprs : body) {
if (exprs.kind.equals("if")) { // <-- Add
Object val = if_(exprs, ret);
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 {
expression(exprs);
}
}
return null;
}
Die if_ ()
Methode, die die if-Anweisung ausführt.
token.left
enthält den bedingten Ausdruck.
Die Methode isTrue ()
bestimmt, ob der bedingte Ausdruck wahr ist.
Wenn der bedingte Ausdruck wahr ist, führen Sie "token.block" aus.
Wenn false, führen Sie token.blockOfElse
aus.
Interpreter.java
public Object if_(Token token, boolean[] ret) throws Exception {
List<Token> block;
if (isTrue(token.left)) {
block = token.block;
} else {
block = token.blockOfElse;
}
if (block != null) {
return body(block, ret);
} else {
return null;
}
}
public boolean isTrue(Token token) throws Exception {
return 0 != value(expression(token));
}
Das folgende Programm verwendet die obige Implementierung
function f(a) {
if (a) {
return 3
} else {
return 4
}
}
println(f(1))
println(f(0))
Drucken von "3" und "4" nacheinander auf die Standardausgabe.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "function f(a) {";
text += " if (a) {";
text += " return 3";
text += " } else {";
text += " return 4";
text += " }";
text += "}";
text += "println(f(1))";
text += "println(f(0))";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 3
// --> 4
}
Das ist alles für die Implementierung. Vielen Dank.
Die vollständige Quelle finden Sie hier.
Calc https://github.com/quwahara/Calc/tree/article-10-if-r2/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht Vergleichs- und logischen Operatoren ** http://qiita.com/quwahara/items/162d01c5af7c69cfa0ed
Recommended Posts