Im vorherigen Artikel Unterstützt den Umfang. Diesmal entspricht es dem Funktionsausdruck. Ich werde versuchen, einen Abschluss zu machen, indem ich den im vorherigen Artikel unterstützten Bereich verwende.
Bestätigen Sie, was Sie mit der Unterstützung von Funktionsausdrücken tun möchten.
Zum Beispiel gibt es das folgende Programm.
Der vom äußeren Funktionsausdruck zurückgegebene innere Funktionsausdruck wird der Variablen "Zähler" zugewiesen.
Jedes Mal, wenn Sie "counter" als Funktion aufrufen
Der im äußeren Funktionsausdruck definierte Wert der Variablen c
wird hinzugefügt.
Der erste println (counter ())
druckt 1
,
Der nächste println (counter ())
gibt 2
aus.
counter = (function() {
var c = 0
return function() {
c = c + 1
return c
}
})()
println(counter())
println(counter())
Wir werden überlegen, wie es in der Reihenfolge Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.
Die syntaktische Analyse eines Funktionsausdrucks unterscheidet sich von der syntaktischen Analyse einer Funktionsdefinition mit oder ohne Funktionsnamen. Der Unterschied spiegelt sich in der Syntaxanalyse der Funktionsdefinition wider.
Führt ein Objekt ein, das einen Funktionsausdruck darstellt. Das Objekt kann als Wert behandelt werden und unterstützt auch Funktionsaufrufe. Bisher wurde nur "Integer" als Wert behandelt. Funktionsausdrücke müssen ebenfalls als Werte behandelt werden.
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.
Eine Modifikation der Methode func ()
, die die Funktionsdefinition analysiert.
Der geänderte Teil ist die erste if-Anweisung.
Bei der ursprünglichen Implementierung wurde erwartet, dass auf das Funktionstoken das Ident-Token folgt, bei dem es sich um den Funktionsnamen handelt.
Im Fall eines Funktionsausdrucks folgt auf das "Funktionstoken" das "(" -Token).
Die Beurteilung der if-Anweisung erfolgt nach dem "Funktionstoken"
Wenn nicht, habe ich es geändert, um die Funktionsdefinition zu analysieren.
Parser.java
private Token func(Token token) throws Exception {
if (token().value.equals("(")) {
token.kind = "fexpr";
} else {
token.kind = "func";
token.ident = ident();
}
consume("(");
token.params = new ArrayList<Token>();
if (!token().value.equals(")")) {
token.params.add(ident());
while (!token().value.equals(")")) {
consume(",");
token.params.add(ident());
}
}
consume(")");
token.block = body();
return token;
}
Interpreter.java
Eine Implementierung von Interpreter.java.
Eine Änderung der Klasse "Variable", die eine Variable darstellt. Bisher hat sich der Wert nur mit "Integer" befasst. Da wir einen Funktionsausdruck einführen, muss der Funktionsausdruck auch als Wert behandelt werden. Typ der Feldvariablen "Wert", die verarbeitet werden kann, Geändert von "Integer" zu "Object".
Interpreter.java
public static class Variable {
public String name;
public Object value;
@Override
public String toString() {
return name + " " + value;
}
}
Dies ist eine Modifikation der Methode expression ().
Es ist ein Prozess, der nach der Bedeutung (Art) des Tokens verzweigt, der den Ausdruck darstellt.
Unter // Add
wurde ein Zweig für das Token fexpr
hinzugefügt, der einen Funktionsausdruck darstellt.
Interpreter.java
public Object expression(Token expr) throws Exception {
if (expr.kind.equals("digit")) {
return digit(expr);
} else if (expr.kind.equals("ident")) {
return ident(expr);
} else if (expr.kind.equals("func")) {
return func(expr);
// Add
} else if (expr.kind.equals("fexpr")) {
return fexpr(expr);
} else if (expr.kind.equals("paren")) {
return invoke(expr);
} else if (expr.kind.equals("sign") && expr.value.equals("=")) {
return assign(expr);
} else if (expr.kind.equals("unary")) {
return unaryCalc(expr);
} else if (expr.kind.equals("sign")) {
return calc(expr);
} else {
throw new Exception("Expression error");
}
}
Es wurde die Methode "fexpr ()" hinzugefügt, die für den Zweig aufgerufen wird, der durch die obige Methode "expression" hinzugefügt wurde. Erstellt ein Objekt, das einen Funktionsausdruck darstellt. Um den übergeordneten Bereich des Funktionsausdrucks beibehalten zu können, wird der Funktionsausdruck beim Schließen verwendet Der aktuelle "Interpreter" wird geklont und in "func.context" gespeichert.
Interpreter.java
public Object fexpr(Token token) throws Exception {
List<String> paramCheckList = new ArrayList<String>();
for (Token p : token.params) {
String param = p.value;
if (paramCheckList.contains(param)) {
throw new Exception("Parameter name was used");
}
paramCheckList.add(param);
}
DynamicFunc func = new DynamicFunc();
func.context = new Interpreter();
func.context.global = global;
func.context.local = local;
func.context.body = body;
func.params = token.params;
func.block = token.block;
return func;
}
Eine Modifikation der Methode func ()
, mit der eine Instanz erstellt wird, die eine Funktionsdefinition darstellt.
Wenn die Methode "fexpr ()" den aktuellen "Interpreter" klont,
Ich habe es auch geändert, um unter "// Update" zu klonen.
Interpreter.java
public Object func(Token token) throws Exception {
String name = token.ident.value;
if (local.functions.containsKey(name)) {
throw new Exception("Name was used");
}
if (local.variables.containsKey(name)) {
throw new Exception("Name was used");
}
List<String> paramCheckList = new ArrayList<String>();
for (Token p : token.params) {
String param = p.value;
if (paramCheckList.contains(param)) {
throw new Exception("Parameter name was used");
}
paramCheckList.add(param);
}
DynamicFunc func = new DynamicFunc();
// Update
func.context = new Interpreter();
func.context.global = global;
func.context.local = local;
func.context.body = body;
func.name = name;
func.params = token.params;
func.block = token.block;
local.functions.put(name, func);
return null;
}
Ändern Sie die Methode "value ()" und fügen Sie die Methode "integer ()" hinzu.
Die Methode value ()
gibt den Wert zurück, wenn das Argument der Wert selbst ist, z. B. 1
.
Wenn es sich um eine Variable handelt, wird der von der Variablen gehaltene Wert zurückgegeben.
Bisher haben wir uns nur mit "Integer" für Werte befasst.
Der entsprechende Funktionsausdruck ist auch der von dieser Methode zurückgegebene Wert.
Ändern Sie den Rückgabetyp der Methode value ()
von Integer
in Object
.
Jetzt können sowohl Ganzzahl- als auch Funktionsausdrücke mit der Methode value () zurückgegeben werden.
Der Grund für das Hinzufügen der Methode "integer ()" ist
Dies liegt daran, dass der Rückgabewert der Methode value ()
von Integer
in Object
geändert wurde.
Bisher wurde der Rückgabewert der Methode value ()
durch Integer
garantiert.
Der Wechsel von "Integer" zu "Object" garantiert dies nicht mehr.
Stattdessen garantiert die Methode "integer ()", dass der Wert "Integer" ist.
Um diese Änderungen zu beeinflussen und sicherzustellen, dass der Wert "Integer" ist
Die value ()
Methode wurde aufgerufen,
Auch mit der Methode "unaryCalc ()" und der Methode "calc ()"
Ich ändere stattdessen die Methode "integer ()".
Interpreter.java
public Object value(Object value) throws Exception {
if (value instanceof Integer) {
return value;
} else if (value instanceof Func) {
return value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return value(v.value);
}
throw new Exception("right value error");
}
public Integer integer(Object value) throws Exception {
if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return integer(v.value);
}
throw new Exception("right value error");
}
Dies ist eine Modifikation der Methode func ()
.
Eine Methode, die garantiert, dass das Argument eine Funktion ist.
Zu "else if", wenn das Argument vom Typ "Variable" ist
Verarbeitung hinzugefügt, um sicherzustellen, dass der Wert eine Funktion ist.
Interpreter.java
public Func func(Object value) throws Exception {
if (value instanceof Func) {
return (Func) value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return func(v.value);
} else {
throw new Exception("Not a function");
}
}
Dies ist eine Änderung der isTrue-Methode. Die isTrue-Methode ist ein Prozess, der einen Wert in einen booleschen Wert auflöst. Bisher war der Wert nur "Integer", aber jetzt, da Funktionen auch als Werte behandelt werden, Wenn der Wert eine Funktion war, wurde er einfach in "wahr" aufgelöst.
Interpreter.java
public boolean isTrue(Object value) throws Exception {
if (value instanceof Integer) {
return 0 != ((Integer) value);
} else if (value instanceof Func) {
return true;
} else {
return false;
}
}
Das folgende Programm verwendet die obige Implementierung
counter = (function() {
var c = 0
return function() {
c = c + 1
return c
}
})()
println(counter())
println(counter())
Und
Der erste println (counter ())
druckt 1
,
Der nächste println (counter ())
gibt 2
aus.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "counter = (function() {";
text += " var c = 0";
text += " return function() {";
text += " c = c + 1";
text += " return c";
text += " }";
text += "})()";
text += "println(counter())";
text += "println(counter())";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 1
// --> 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-14-function-expression-r3/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht einer Zeichenkette ** http://qiita.com/quwahara/items/ddcbc8c37b9d442fe0f2
Recommended Posts