[JAVA] 18 Entspricht der JSON-ähnlichen Objektdefinition

Einführung

Ich möchte JSON-ähnliche Objektdefinitionen unterstützen. Dieser Artikel ist eine Fortsetzung von "Entspricht Array".

Was Sie mit der Unterstützung von JSON-ähnlichen Objektdefinitionen tun möchten

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.

Wie zu implementieren

Wir werden überlegen, wie es in der Reihenfolge der Phrasenanalyse (Lexer), Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.

So implementieren Sie eine Phrasenanalyse (Lexer)

Da es keine Funktion zum Analysieren von : gibt, fügen Sie sie hinzu.

So implementieren Sie Parser

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.

So implementieren Sie Interpreter

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.

Versuchen Sie, in Java zu implementieren

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.

abschließend

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

18 Entspricht der JSON-ähnlichen Objektdefinition
19 Entspricht der Objekterstellung
17 Entspricht einem Array
Entspricht dem Geltungsbereich
Entspricht 15 Zeichenfolgen
8 Entspricht mehreren Argumenten
10 Entspricht der if-Anweisung
14 Entspricht einem Funktionsausdruck
5 Entspricht priorisierten Klammern
16 Entspricht dem Methodenaufruf
9 Entspricht dem Rückgabewert
12 Entspricht der while-Anweisung
Konvertieren Sie ein serialisierbares Objekt in Byte []
20 Entspricht statischen Methodenaufrufen
11 Entspricht Vergleichs- und logischen Operatoren
Konvertieren Sie das Ruby-Objekt in das JSON-Format