[JAVA] 14 Entspricht einem Funktionsausdruck

Einführung

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.

Was Sie mit der Unterstützung von Funktionsausdrücken tun möchten

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())

Wie zu implementieren

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

So implementieren Sie Parser

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.

So implementieren Sie Interpreter

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.

Versuchen Sie, in Java zu implementieren

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.

abschließend

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

14 Entspricht einem Funktionsausdruck
17 Entspricht einem Array
Entspricht dem Geltungsbereich
Entspricht 15 Zeichenfolgen
8 Entspricht mehreren Argumenten
10 Entspricht der if-Anweisung
5 Entspricht priorisierten Klammern
19 Entspricht der Objekterstellung
16 Entspricht dem Methodenaufruf
9 Entspricht dem Rückgabewert
Java zum Spielen mit Function
So fügen Sie die ActionText-Funktion hinzu
12 Entspricht der while-Anweisung
[Java] Einführung in den Lambda-Ausdruck
11 Entspricht Vergleichs- und logischen Operatoren
[Einführung in Java] Über Lambda-Ausdrücke
Die Funktion ist sehr einfach zu bedienen
Versuchen Sie, die iOS14-Widget-Funktion zu implementieren
So fügen Sie die Löschfunktion hinzu
Dinge, die in bedingten Ausdrücken zu beachten sind
Verwendung von Java-Lambda-Ausdrücken
to_ ○
Ich habe versucht, Java-Lambda-Ausdrücke zusammenzufassen
Versuchen Sie, eine Anmeldefunktion mit Spring-Boot zu implementieren
So implementieren Sie TextInputLayout mit Validierungsfunktion
[Verarbeitung × Java] Verwendung der Funktion
Einfach mit regulären Java-Ausdrücken zu stolpern