Im vorherigen Artikel Mehrere Argumente unterstützen. Ich möchte weiterhin dem Rückgabewert der Funktion entsprechen.
Stellen Sie sicher, dass Sie es einfach machen möchten. Wenn Sie beispielsweise ein Programm wie das folgende haben, wird der Rückgabewert des Funktionsaufrufs "add3 ()" zurückgegeben und der Variablen "v" zugewiesen. Wir wollen den Wert "6" der Variablen "v" an die Standardausgabe ausgeben.
function add3(a1, a2, a3) {
return a1 + a2 + a3
}
v = add3(1,2,3)
println(v)
Es ist dasselbe wie im vorherigen Artikel, dass es nicht dem Umfang der Variablen entspricht.
Wir werden überlegen, wie es in der Reihenfolge Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann. Die Phrasenanalyse (Lexer) wird nicht geändert.
Bei der Implementierung der Syntaxanalyse von "return" müssen Sie auf die Syntax von "return" achten.
Die Syntax von "return" wird manchmal als Rückgabewert angegeben, z. B. "return 1".
In einigen Fällen wird nur "return" angegeben und kein Rückgabewert angegeben.
Wenn die Angabe von nur "return" in der Syntax korrekt ist,
Wenn "return 1" geschrieben ist,
Es kann als Syntax verwendet werden, wenn ein Rückgabewert angegeben wird.
In Anbetracht dessen, dass "1" eine von "return" unabhängige Syntax ist,
Es kann als Syntax verwendet werden, wenn der Rückgabewert nicht angegeben ist.
Um diese Situation leicht zu lösen, werden wir einen schlampigen Ansatz verfolgen.
Die Methode ist, wenn das }
Token nach dem return
Token kommt,
Wird als "Rückgabe" betrachtet, wenn kein Rückgabewert angegeben ist.
Im Gegenteil, wenn das Token "}" nicht kommt, wird es als "return" angesehen, wenn der Rückgabewert angegeben wird.
Das Parsen, wenn kein Rückgabewert angegeben ist, wird wie ein Variablenname behandelt.
Das Parsen, wenn ein Rückgabewert angegeben wird, wird genauso behandelt wie ein einzelner Termoperator wie "-1".
Der Interpreter nimmt Änderungen an der Methode body ()
vor, die Ausdrücke nacheinander ausführt.
Wenn es sich um ein "Return" -Token in "body ()" handelt,
Unterbricht die sequentielle Ausführung und kehrt zum Anrufer zurück.
Außerdem kann "return" nur innerhalb einer Funktion aufgerufen werden.
Es wird auch bestimmt, ob es sich um einen Rückruf innerhalb der Funktion handelt.
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 "return" ein reserviertes Wort ist, Ich habe "return" hinzugefügt, wo es "<-Update" gibt.
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" }); // <-- Update
}
Es ist eine Änderung des zu analysierenden Teils.
Es wurde eine if-Anweisung hinzugefügt, die return
dahin analysiert, wo <-Add
ist.
Es sei denn, das Token neben dem "Return" -Token ist "eob", was die Art der schließenden schließenden Klammer darstellt
Beurteilt als "Rückgabe", die einen Wert zurückgibt, und enthält das Token, das der Rückgabewert im Feld "Links" 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")) { // <-- Add
token.kind = "ret";
if (!token().kind.equals("eob")) {
token.left = expression(0);
}
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.");
}
}
Interpreter.java
Eine Implementierung von Interpreter.java.
Dies ist eine Änderung der Methode body ()
, die Ausdrücke nacheinander ausführt.
body ()
war ein void
Typ, der keinen Rückgabewert zurückgibt, aber in einen Object
Typ geändert wurde, damit er einen Rückgabewert zurückgeben kann.
Boolean [] ret
zur Signatur von body ()
hinzugefügt.
ret
hat zwei Rollen.
Das erste ist, dass, wenn "ret" nicht "null" ist, "return" möglich ist.
Das zweite ist, dass wenn es "return" wäre, es an den Anrufer weitergeben würde, dass es "return" wäre.
Der Grund, ret
zu einem Array-Typ von boolean
zu machen, besteht darin, einen Wert an den Aufrufer zurückzugeben.
Es wird auf böse Weise verwendet, nicht zum Zweck der Verwendung des ursprünglichen Arrays.
Ich wollte es einfacher machen, wie ein Argument mit dem Schlüsselwort "ref" in C # zu arbeiten.
Das erste, was in "for" ist, ist zu bestimmen, ob das Token, das nacheinander ausgeführt werden soll, "return" ist. Wenn es nicht "return" ist, ist es der gleiche Prozess wie der vorherige "body ()". Wenn es sich um "Rückkehr" handelt, wird beurteilt, ob eine "Rückkehr" möglich ist. Ersetzen Sie "true", um dem Anrufer mitzuteilen, dass er zu "ret" zurückkehren soll. Wenn es einen Rückgabewert für "return" gibt, führen Sie das Token aus, das der Rückgabewert ist, mit "expression ()".
Interpreter.java
public Object body(List<Token> body, boolean[] ret) throws Exception {
for (Token exprs : body) {
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;
}
Dies ist eine Modifikation der Methode "invoke ()" der Klasse "DynamicFunc".
<-Update
ist die Änderung.
Unterstützt das Ändern der Signatur und des Rückgabewerts der Methode "body ()".
Das Zuweisen einer Instanz zum Argument "ret" zeigt an, dass die Situation "zurückgegeben" werden kann.
Der Rückgabewert von "body ()" wird genauso verwendet wie der Rückgabewert von "invoke ()".
Interpreter.java
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]; // <-- Update
return context.body(block, ret); // <-- Update
}
}
Dies ist eine Modifikation der Methode run ()
.
<-Update
ist die Änderung.
Unterstützt das Ändern der Signatur und des Rückgabewerts der Methode "body ()".
Die Situation, in der run ()
aufgerufen wird, befindet sich nicht innerhalb der Funktion.
Da die Situation nicht "return" sein kann, gibt das formale Argument "ret" "null" an.
Interpreter.java
public Map<String, Variable> run() throws Exception {
body(body, null); // <-- Update
return variables;
}
Das folgende Programm verwendet die obige Implementierung
function add3(a1, a2, a3) {
return a1 + a2 + a3
}
v = add3(1,2,3)
println(v)
Drucken des der Variablen "v" zugewiesenen Werts "6" an die Standardausgabe.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "function add3(a1, a2, a3) {";
text += " return a1 + a2 + a3";
text += "}";
text += "v = add3(1,2,3)";
text += "println(v)";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 6
}
Das ist alles für die Implementierung. Vielen Dank.
Die vollständige Quelle finden Sie hier.
Calc https://github.com/quwahara/Calc/tree/article-9-return-r2/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht der if-Anweisung ** http://qiita.com/quwahara/items/96a68cdee4f2a0452836
Recommended Posts