Einfache Funktionsdefinitionen und Aufrufe wurde in einem früheren Artikel hinzugefügt. Zu dieser Zeit gab es nur ein Funktionsargument. Dieses Mal werden keine Argumente oder zwei oder mehr Argumente unterstützt.
Stellen Sie sicher, dass Sie es einfach machen möchten.
Wenn Sie beispielsweise ein Programm wie das folgende haben, ist die Funktion add3 ()
definiert und wir möchten 6
an die Standardausgabe ausgeben.
v = 0
function add3(a1, a2, a3) {
v = a1 + a2 + a3
}
add3(1,2,3)
println(v)
Die mangelnde Unterstützung für Funktionsrückgabewerte und variable Bereiche bleibt in früheren Artikeln gleich.
Wir werden überlegen, wie es in der Reihenfolge der Phrasenanalyse (Lexer), Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.
Da es bisher keine Funktion zum Analysieren von Kommas und `` in der Phrasenanalyse der Implementierung gibt, werden wir sie implementieren.
Wir werden Änderungen am Implementierungsteil der Syntaxanalyse von Funktionsdefinitionen und Funktionsaufrufen vornehmen, die wir im vorherigen Artikel hinzugefügt haben. Beide betrachten den Fall also nur mit einem Argument Ändern Sie die Parsing-Implementierung so, dass keine oder mehrere Argumente berücksichtigt werden.
Dies ist auch eine Klasse, die die im vorherigen Artikel hinzugefügte Funktion darstellt. Verwenden Sie diese Option, um Änderungen an dem Teil vorzunehmen, der die Funktion definiert und aufruft. Alle betrachten nur den Fall eines Arguments Ändern Sie diese Option, um keine oder mehrere Argumente zu berücksichtigen.
Fahren Sie mit der Implementierung fort. Informationen zur Phrasenanalyse (Lexer), Syntaxanalyse (Parser), Interpreter (Interpreter) Schauen wir uns die Änderungen und Ergänzungen der Reihe nach an.
Lexer.java
Eine Implementierung von Lexer.java.
Fügen Sie zunächst die Funktion zum Analysieren von Kommas und ,
hinzu.
Lexer.java
private boolean isSymbolStart(char c) {
return c == ',';
}
private Token symbol() throws Exception {
Token t = new Token();
t.kind = "symbol";
t.value = Character.toString(next());
return t;
}
Ich werde den Aufrufteil der Kommaanalyse hinzufügen. Die Implementierung von Lexer.java ist abgeschlossen.
Lexer.java
public Token nextToken() throws Exception {
skipSpace();
if (isEOT()) {
return null;
} else if (isSignStart(c())) {
return sign();
} else if (isDigitStart(c())) {
return digit();
} else if (isIdentStart(c())) {
return ident();
} else if (isParenStart(c())) {
return paren();
} else if (isCurlyStart(c())) {
return curly();
} else if (isSymbolStart(c())) {
return symbol();
} else {
throw new Exception("Not a character for tokens");
}
}
Parser.java
Die Methode func ()
wurde geändert, um die Funktionsdefinition zu analysieren.
Vor der Erläuterung der Änderung der func ()
-Methode, da die Änderung der Token
-Klasse relevant ist
Ich werde erklären, wie man die Token-Klasse ändert.
Das Analyseergebnis der Funktionsdefinition ist im Argument "Token" zusammengefasst.
Im vorherigen Artikel habe ich eine Feldvariable namens "param" hinzugefügt, die ein formales Argument für die "Token" -Klasse darstellt.
Um mehrere Argumente zu unterstützen, wird dieses Mal das Feld "param" abgeschafft und eine Feldvariable mit dem Namen "params" vom Typ "List func ()
.
Die wichtigsten Änderungen sind "<-Update".
Die Syntaxanalyse dort ist wie folgt:
(Nach dem
Token, nach
)
Token bald kommt, gibt es kein Argument)
Token nicht sofort erhalten, haben Sie zuerst mindestens ein Argument)
Token nicht kommt, werden zwei oder mehr Argumente fortgesetzt.Und analysieren.
Parser.java
private Token func(Token token) throws Exception {
token.kind = "func";
token.ident = ident();
consume("(");
token.params = new ArrayList<Token>();
if (!token().value.equals(")")) { // <-- Update
token.params.add(ident());
while (!token().value.equals(")")) {
consume(",");
token.params.add(ident());
}
}
consume(")");
consume("{");
token.block = block();
consume("}");
return token;
}
Eine Implementierung der Syntaxanalyse für den Teil, der den Funktionsaufruf ausführt. Die Hauptänderung ist "<-Update", und Sie können sehen, dass die Implementierungsmethode fast dieselbe ist wie die Syntaxanalyse der Funktionsdefinition zuvor.
Parser.java
private Token bind(Token left, Token operator) throws Exception {
if (binaryKinds.contains(operator.kind)) {
operator.left = left;
int leftDegree = degree(operator);
if (rightAssocs.contains(operator.value)) {
leftDegree -= 1;
}
operator.right = expression(leftDegree);
return operator;
} else if (operator.kind.equals("paren") && operator.value.equals("(")) {
operator.left = left;
operator.params = new ArrayList<Token>();
if (!token().value.equals(")")) { // <-- Update
operator.params.add(expression(0));
while (!token().value.equals(")")) {
consume(",");
operator.params.add(expression(0));
}
}
consume(")");
return operator;
} else {
throw new Exception("The token cannot place there.");
}
}
Interpreter.java
Eine Implementierung von Interpreter.java.
Eine Modifikation der abstrakten Klasse der Klasse, die die Funktion darstellt.
Das Argument der Methode "invoke ()" nach mehreren Argumenten,
Geändert von Object arg
zu List <Object> args
.
Interpreter.java
public static abstract class Func {
public String name;
abstract public Object invoke(List<Object> args) throws Exception;
}
Ich habe die Methode "invoke ()" der Klasse "Println" geändert, um sie an die Änderung der abstrakten Klasse anzupassen.
Interpreter.java
public static class Println extends Func {
public Println() {
name = "println";
}
@Override
public Object invoke(List<Object> args) throws Exception {
Object arg = args.size() > 0 ? args.get(0) : null;
System.out.println(arg);
return null;
}
}
In ähnlicher Weise wurde die Methode "invoke ()" der Klasse "DynamicFunc" geändert, um sie an die Änderung der abstrakten Klasse anzupassen.
Wechseln.
Außerdem wurde die Feldvariable "param" abgeschafft, damit mehrere Argumente ausgedrückt werden können, und "params" vom Typ "List
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;
}
}
context.body(block);
return null;
}
}
Es ist eine Änderung des Funktionsdefinitionsteils. Die Hauptänderungen sind "<-Add" und "<-Update". In der for-Anweisung wird wiederholt, um mehrere Argumente zu verarbeiten.
Interpreter.java
public Object func(Token token) throws Exception {
String name = token.ident.value;
if (functions.containsKey(name)) {
throw new Exception("Name was used");
}
if (variables.containsKey(name)) {
throw new Exception("Name was used");
}
List<String> paramCheckList = new ArrayList<String>(); // <-- Add
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 = this;
func.name = name;
func.params = token.params; // <-- Update
func.block = token.block;
functions.put(name, func);
return null;
}
Das folgende Programm verwendet die obige Implementierung
v = 0
function add3(a1, a2, a3) {
v = a1 + a2 + a3
}
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 += "v = 0";
text += "function add3(a1, a2, a3) {";
text += " v = a1 + a2 + a3";
text += "}";
text += "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-8-multiple-arguments-r2/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht dem Rückgabewert ** http://qiita.com/quwahara/items/1db9a5b880fd36dcfd3c
Recommended Posts