Ich möchte JSON-ähnliche Objektdefinitionen unterstützen. Dieser Artikel ist eine Fortsetzung von "Entspricht Array".
Bestätigen Sie, was Sie mit einer JSON-ähnlichen Objektdefinition tun möchten.
Zum Beispiel gibt es das folgende Programm.
Erstellen Sie ein Objekt in der ersten Zeile und weisen Sie es der Variablen obj
zu.
Die 5. Zeile gibt die Anzahl der Elemente "2" des Objekts aus.
In der 6. Zeile wollen wir den Wert ausgeben, der dem "Schlüssel2" des Objekts, "Wert2", entspricht.
var obj = {
"key1": "value1",
"key2": "value2",
}
println(obj.size())
println(obj["key2"])
Der Zugriff auf Werte von .
wie obj.key2
wird nicht unterstützt.
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 :
gibt, fügen Sie sie hinzu.
Implementierungen für die syntaktische Analyse entsprechen der Syntax zum Generieren von Objektdefinitionen. Die Syntax für den Zugriff auf die Elemente eines Objekts wird durch die Entsprechung zur Syntax für den Zugriff auf das vorherige Array abgedeckt.
Die Syntax zum Erstellen eines Objekts beginnt mit einem {
Token.
Implementieren Sie es in der Methode lead ()
, genau wie das andere erste Token syntaktisch bestimmt wird.
Ich habe mich entschieden, "LinkedHashMap <String, Object>" als eigentliches Objekt zu verwenden.
Die Verarbeitung für die Syntax implementiert die Erstellung des Objekts und den Zugriff auf die Objektelemente.
Bei der Objektgenerierung wird "LinkedHashMap <String, Object>" generiert und Elemente hinzugefügt.
Um auf das Objektelement zuzugreifen, rufen Sie die Methode LinkedHashMap <String, 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ügt die Phrasenanalysefunktion von :
hinzu.
Ändern Sie die Methode isSymbolStart ().
Erkennung von :
hinzugefügt.
Lexer.java
private boolean isSymbolStart(char c) {
// Update
return c == ',' || c == ':';
}
Das ist alles, um Lexer.java zu ändern.
Parser.java
Eine Implementierung von Parser.java.
Implementiert das Parsen der Objekterstellungssyntax.
Von der lead ()
Methode, die die durch das erste Token bestimmte Syntax implementiert
Aufruf der Methode "newMap ()" hinzugefügt, die eine Syntaxanalyse zur Objekterstellung unter "// Add" 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;
} else if (token.kind.equals("bracket") && token.value.equals("[")) {
return newArray(token);
// Add
} else if (token.kind.equals("curly") && token.value.equals("{")) {
return newMap(token);
} else {
throw new Exception("The token cannot place there.");
}
}
Die Methode newMap ()
wurde hinzugefügt, um eine Syntaxanalyse zur Objekterstellung durchzuführen.
Sammeln Sie die durch "," getrennten Elemente in den "Parametern" des "{" -Tokens, bis das "}" -Token erreicht ist.
Das Element enthält das Schlüsselelement links vom Token ":" und das Wertelement rechts.
Fügen Sie das ":" - Token zu den "Parametern" des "{" -Tokens hinzu.
Der Token-Typ "kind" ist auf "newMap" gesetzt.
Wenn das letzte Element leer ist, wie "[{" key1 ":" value1 "}, {" key2 ":" value2 "},]"
Ich habe versucht, es als Element zu ignorieren.
Parser.java
private Token newMap(Token token) throws Exception {
token.kind = "newMap";
token.params = new ArrayList<Token>();
while(true) {
if (token().value.equals("}")) {
consume("}");
break;
}
Token key = expression(0);
Token colon = consume(":");
colon.left = key;
token.params.add(colon);
if (token().value.equals(",")) {
colon.right = blank;
consume(",");
continue;
}
colon.right = expression(0);
if (token().value.equals(",")) {
consume(",");
continue;
} else {
consume("}");
break;
}
}
return token;
}
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.
Ein newMap ()
Methodenaufruf zur Objekterstellung unter // Add
hinzugefügt.
Die Methode accessArray ()
für den Array-Zugriff unter // Update
wurde in die Methode accessArrayOrMap ()
geändert.
Dies liegt daran, dass die Methode jetzt sowohl Array- als auch Objektzugriff hat.
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);
} else if (expr.kind.equals("blank")) {
return blank(expr);
// Add
} else if (expr.kind.equals("newMap")) {
return newMap(expr);
} else if (expr.kind.equals("newArray")) {
return newArray(expr);
// Update
} else if (expr.kind.equals("bracket")) {
return accessArrayOrMap(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");
}
}
NewMap ()
Methode für die Objekterstellung hinzugefügt.
Erzeugt "LinkedHashMap <String, Object>", fügt ein Element hinzu und gibt es zurück.
Interpreter.java
public Object newMap(Token expr) throws Exception {
Map<String, Object> m = new LinkedHashMap<>();
for (Token item : expr.params) {
String key = identOrString(item.left);
Object value = value(expression(item.right));
m.put(key, value);
}
return m;
}
Es wurde eine identOrString () -Methode hinzugefügt, die garantiert, dass das Argument-Token als Schlüssel des Objekts gültig ist. Wird in der obigen Methode "newMap ()" verwendet. Wenn das Argument-Token selbst ein Bezeichner ist oder das Ergebnis der Ausführung des Argument-Tokens "String" ist, ist es gültig.
Interpreter.java
public String identOrString(Token expr) throws Exception {
if (expr.kind.equals("ident")) {
return expr.value;
} else {
return string(expression(expr));
}
}
Die Methode value ()
der Methode wurde geändert, die garantiert, dass das Argument ein Wert ist.
Das erste "// Hinzufügen" erlaubt jetzt "Map <String, Object>" als Wert.
Interpreter.java
public Object value(Object value) throws Exception {
if (value instanceof Integer) {
return value;
} else if (value instanceof String) {
return value;
} else if (value instanceof List<?>) {
return value;
// Add
} else if (value instanceof Map<?, ?>) {
return value;
} 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");
}
Die accessArray ()
Methode für den Array-Zugriff,
Geändert in die Methode "accessArrayOrMap ()" für den Array- oder Objektzugriff.
Dem Argument expr
wird ein [
Token übergeben.
Wenn das "[" Token "left" "List >" Ist, wird es als Array behandelt, und wenn es "Map ,?>" Ist, wird es als Objekt behandelt.
Das "[" Token "rechts" wird als Index behandelt, wenn es sich um ein Array handelt, und als Schlüssel, wenn es sich um ein Objekt handelt.
Interpreter.java
@SuppressWarnings("unchecked")
public Object accessArrayOrMap(Token expr) throws Exception {
Object v = value(expression(expr.left));
if (v instanceof List<?>) {
List<Object> ar = (List<Object>) v;
Integer index = integer(expression(expr.right));
return ar.get(index);
} else if (v instanceof Map<?, ?>) {
Map<String, Object> map = (Map<String, Object>) v;
String key = string(expression(expr.right));
return map.get(key);
} else {
throw new Exception("accessArrayOrMap error");
}
}
Das folgende Programm verwendet die obige Implementierung
var obj = {
"key1": "value1",
"key2": "value2",
}
println(obj.size())
println(obj["key2"])
Und geben Sie den Wert "v2" aus, der der Anzahl der Elemente "2" und "key2" des Objekts zugeordnet ist.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "var m = {";
text += " key1: \"v1\",";
text += " key2: \"v2\",";
text += "}";
text += "println(m.size())";
text += "println(m[\"key2\"])";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 2
// --> v2
}
Das ist alles für die Implementierung. Vielen Dank.
Die vollständige Quelle finden Sie hier.
Calc https://github.com/quwahara/Calc/tree/article-18-map/Calc/src/main/java
Es gibt einen Folgeartikel.
** Entspricht der Objekterstellung ** http://qiita.com/quwahara/items/ced0e9e2bf487f4021f8
Recommended Posts