Ich möchte eine Reihe grundlegender Datenstrukturen unterstützen. Dieser Artikel ist eine Fortsetzung von "Entspricht Methodenaufrufen".
Bestätigen Sie, was Sie mit der Array-Unterstützung tun möchten.
Zum Beispiel gibt es das folgende Programm.
Erstellen Sie ein Array in der ersten Zeile und weisen Sie es der Variablen ar
zu.
Die zweite Zeile gibt die Array-Länge "3" aus.
Die dritte Zeile zielt darauf ab, das dritte Element des Arrays "c" auszugeben.
var ar = ["a", "b", "c"]
println(ar.size())
println(ar[2])
Wir werden überlegen, wie es in der Reihenfolge der Phrasenanalyse (Lexer), Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.
Da es keine Funktion zum Analysieren von "[" und "]" gibt, fügen Sie sie hinzu.
Die Implementierung für das Parsen von Syntax entspricht zwei Syntaxen: eine, die ein Array erstellt, und eine, die auf Elemente des Arrays zugreift.
Die Syntax zum Generieren eines Arrays beginnt mit einem [
Token.
Implementieren Sie es in der Methode "lead ()", genau wie die anderen ersten Token-Konstrukte.
Die Syntax für den Zugriff auf die Elemente eines Arrays entspricht der Syntax für einen Funktionsaufruf mit einem Argument. Wenn Sie im Programmbeispiel "[]" von "ar [2]" durch "()" ersetzen, erhalten Sie "ar (2)". Sie können sehen, dass sie gleichwertig sind, da sie wie Funktionsaufrufe aussehen. Implementieren Sie es in der Methode bind () auf die gleiche Weise wie die Syntaxanalyse von Funktionsaufrufen.
Da die Entität des Arrays Elemente hinzufügen und die Länge durch die Instanzmethode ermitteln kann, Ich habe mich für ArrayList
Die Verarbeitung für die Syntax implementiert die Generierung des Arrays und den Zugriff auf die Array-Elemente.
Bei der Array-Generierung wird "ArrayList ArrayList <Object> :: get ()
auf.
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 die Phrasenanalysefunktion von "[" und "]" hinzu.
Fügen Sie die Methode "isBracketStart ()" hinzu. Erkennt "[" und "]".
Lexer.java
private boolean isBracketStart(char c) {
return c == '[' || c == ']';
}
Fügen Sie die Methode bracket ()
hinzu.
Analysieren Sie "[" und "]" in Token.
"kind", was für "[" und "]" steht, sollte "Klammer" sein.
Lexer.java
private Token bracket() throws Exception {
Token t = new Token();
t.kind = "bracket";
t.value = Character.toString(next());
return t;
}
Ändern Sie die Methode nextToken ().
Fügen Sie den Aufruf der hinzugefügten Methode zu // Add
hinzu.
Jetzt können Sie "[" und "]" in Token zerlegen.
Lexer.java
public Token nextToken() throws Exception {
skipSpace();
if (isEOT()) {
return null;
} else if (isSignStart(c())) {
return sign();
} else if (isDotStart(c())) {
return dot();
} else if (isDigitStart(c())) {
return digit();
} else if (isStringStart(c())) {
return string();
} else if (isIdentStart(c())) {
return ident();
} else if (isParenStart(c())) {
return paren();
} else if (isCurlyStart(c())) {
return curly();
// Add
} else if (isBracketStart(c())) {
return bracket();
} else if (isSymbolStart(c())) {
return symbol();
} else {
throw new Exception("Not a character for tokens");
}
}
Das ist alles, um Lexer.java zu ändern.
Parser.java
Eine Implementierung von Parser.java.
Fügen Sie eine Definition hinzu, wie es für die Bedeutung des Tokens funktioniert.
Der Ordnungsgrad von [
to // Add
wurde hinzugefügt.
[
Ist besser als arithmetische Operatoren wie +
und *
Weil es die linken und rechten Token stark verbindet,
Es ist 80, was größer ist als der Grad von "+" und "*".
Parser.java
public Parser() {
degrees = new HashMap<>();
degrees.put(".", 80);
degrees.put("(", 80);
degrees.put("[", 80); // Add
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", "string" });
binaryKinds = Arrays.asList(new String[] { "sign", "dot" });
rightAssocs = Arrays.asList(new String[] { "=" });
unaryOperators = Arrays.asList(new String[] { "+", "-", "!" });
reserved = Arrays.asList(new String[] { "function", "return", "if", "else", "while", "break", "var" });
}
Implementiert das Parsen der Syntax zur Sequenzgenerierung.
Von der lead ()
Methode, die die durch das erste Token bestimmte Syntax implementiert
Aufruf der Methode newArray ()
hinzugefügt, die bei // Add
eine Syntaxanalyse zur Array-Generierung durchführt.
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")) {
return while_(token);
} else if (token.kind.equals("ident") && token.value.equals("break")) {
token.kind = "brk";
return token;
} else if (token.kind.equals("ident") && token.value.equals("var")) {
return var(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;
// Add
} else if (token.kind.equals("bracket") && token.value.equals("[")) {
return newArray(token);
} else {
throw new Exception("The token cannot place there.");
}
}
Die Methode newArray ()
wurde hinzugefügt, um eine Syntaxanalyse zur Array-Generierung durchzuführen.
Sammeln Sie die durch "," getrennten Elemente in den "Parametern" des "[" Tokens ", bis das"] "Token erreicht ist.
Der Token-Typ "kind" ist auf "newArray" gesetzt.
Wenn das letzte Element des Arrays leer ist, wie z. B. [1,2,], wird es als Element ignoriert.
Wenn in der Mitte der Sequenz ein leeres Element angegeben wird, z. B. "[1 ,, 3]" im Array,
Der im Voraus vorbereitete "leere" Token wird zugewiesen.
Parser.java
private Token newArray(Token token) throws Exception {
token.kind = "newArray";
token.params = new ArrayList<Token>();
while(true) {
if (token().value.equals("]")) {
consume("]");
break;
}
if (token().value.equals(",")) {
token.params.add(blank);
consume(",");
continue;
}
token.params.add(expression(0));
if (token().value.equals(",")) {
consume(",");
continue;
} else {
consume("]");
break;
}
}
return token;
}
Ich habe das leere Token hinzugefügt, das das leere Element darstellt, das zuvor zur statischen Feldvariablen ausgegeben wurde.
Parser.java
public static Token blank;
static {
blank = new Token();
blank.kind = "blank";
blank.value = "";
}
Implementiert die Array-Zugriffssyntax.
Von der bind ()
Methode, die die Funktionsaufrufsyntax usw. analysiert
Array-Zugriffssyntaxanalyse zu // Add
hinzugefügt.
Für die "Linke" des "Operators", die dem "[" Token entspricht, wird das Token zugewiesen, das dem "ar" des Arrays selbst entspricht, beispielsweise "ar [2]".
Weisen Sie für "Recht" des "Operators" den Index zu, auf den zugegriffen werden soll, z. B. das Token, das "2" von "ar [2]" entspricht.
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(")")) {
operator.params.add(expression(0));
while (!token().value.equals(")")) {
consume(",");
operator.params.add(expression(0));
}
}
consume(")");
return operator;
// Add
} else if (operator.kind.equals("bracket") && operator.value.equals("[")) {
operator.left = left;
operator.right = expression(0);
consume("]");
return operator;
} else {
throw new Exception("The token cannot place there.");
}
}
Das ist alles für das Ändern von Parser.java.
Interpreter.java
Eine Implementierung von Interpreter.java.
Dies ist eine Modifikation der Methode expression ().
Es ist ein Prozess, der nach der Bedeutung (Art) des Tokens verzweigt, das den Ausdruck darstellt.
Unter // Add
,
Rufen Sie die Methode blank ()
für leere Elemente auf.
Rufen Sie die newArray () -Methode zur Array-Generierung auf.
Der Methodenaufruf accessArray ()
für den Array-Zugriff wurde hinzugefügt.
Interpreter.java
public Object expression(Token expr) throws Exception {
if (expr.kind.equals("digit")) {
return digit(expr);
} else if (expr.kind.equals("string")) {
return string(expr);
} else if (expr.kind.equals("ident")) {
return ident(expr);
// Add
} else if (expr.kind.equals("blank")) {
return blank(expr);
// Add
} else if (expr.kind.equals("newArray")) {
return newArray(expr);
// Add
} else if (expr.kind.equals("bracket")) {
return accessArray(expr);
} else if (expr.kind.equals("func")) {
return func(expr);
} 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 if (expr.kind.equals("dot")) {
return dot(expr);
} else {
throw new Exception("Expression error");
}
}
Es wurde eine blank ()
Methode für leere Elemente hinzugefügt.
Es wird einfach "null" zurückgegeben.
Interpreter.java
public Object blank(Token token) {
return null;
}
NewArray ()
Methode zur Array-Generierung hinzugefügt.
Erzeugt "ArrayList
Interpreter.java
public Object newArray(Token expr) throws Exception {
List<Object> a = new ArrayList<>();
for (Token item : expr.params) {
a.add(value(expression(item)));
}
return a;
}
Die Methode value ()
der Methode wurde geändert, die garantiert, dass das Argument ein Wert ist.
Das erste // Add
erlaubt jetzt ArrayList <Object>
als Wert.
Das nächste // Add
erlaubt jetzt null
als Wert.
Interpreter.java
public Object value(Object value) throws Exception {
if (value instanceof Integer) {
return value;
} else if (value instanceof String) {
return value;
// Add
} else if (value instanceof List<?>) {
return value;
// Add
} else if (value == null) {
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");
}
AccessArray ()
Methode für den Array-Zugriff hinzugefügt.
Dem Argument expr
wird ein [
Token übergeben.
Das [
Token left
wird in das Array selbst aufgelöst.
Das "[" Token "-Rechte" wird in den Index aufgelöst, auf den zugegriffen werden soll.
Verwenden Sie sie und verwenden Sie die Methode ArrayList <Object> :: get ()
, um die Elemente abzurufen und zurückzugeben.
Interpreter.java
public Object accessArray(Token expr) throws Exception {
List<Object> ar = array(expression(expr.left));
Integer index = integer(expression(expr.right));
return ar.get(index);
}
Die Methode array ()
wurde hinzugefügt, eine Methode, die garantiert, dass das Argument ein Array ist (List <Object>
).
Ich werde garantieren, dass das, worauf mit der vorherigen accessArray ()
-Methode zugegriffen wird, ein Array ist.
Interpreter.java
@SuppressWarnings("unchecked")
public List<Object> array(Object value) throws Exception {
if (value instanceof List<?>) {
return (List<Object>) value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return array(v.value);
}
throw new Exception("right value error");
}
Das folgende Programm verwendet die obige Implementierung
var ar = ["a", "b", "c"]
println(ar.size())
println(ar[2])
Ausgabe der Länge des Arrays "3" und des dritten Elements des Arrays "c".
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "var ar = [\"a\", \"b\", \"c\"]";
text += "println(ar.size())";
text += "println(ar[2])";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 3
// --> c
}
Das ist alles für die Implementierung. Vielen Dank.
Die vollständige Quelle finden Sie hier.
Calc https://github.com/quwahara/Calc/tree/article-17-array/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht der JSON-ähnlichen Objektdefinition ** http://qiita.com/quwahara/items/5e6cee17b7b03bd994ee
Recommended Posts