[JAVA] 12 Entspricht der while-Anweisung

Einführung

In früheren Artikeln Vergleich und logische Operatoren und Unterstützt if-Anweisungen ) Hat. Ich möchte die while-Anweisung als Erweiterung davon unterstützen.

Was Sie mit while-Anweisungsunterstützung tun möchten

Stellen Sie sicher, dass Sie es einfach machen möchten. Zum Beispiel, wenn Sie ein Programm wie das folgende haben Wiederholen Sie dies mit der while-Anweisung, überspringen Sie die Wiederholung, wenn "v" zu "2" wird, und versuchen Sie, "2" mit "println ()" auszugeben.

v = 0
while (v < 4) {
  v = v + 1
  if (v == 2) {
    break
  }
}
println(v)

Wie zu implementieren

Wie zu implementieren Die Implementierung der while-Anweisung entspricht fast der Korrespondenz der if-Anweisung. Die Implementierung der break-Anweisung entspricht fast der Unterstützung für Funktionsrückgabewerte. Eine ausführliche Erklärung finden Sie hier. Lassen Sie uns diese sofort implementieren.

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. Fügen Sie eine Definition hinzu, wie es für die Bedeutung des Tokens funktioniert. Da "while" und "break" reservierte Wörter sind, habe ich sie zu "<-Update" hinzugefügt.

Parser.java


    public Parser() {
        degrees = new HashMap<>();
        degrees.put("(", 80);
        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" });
        binaryKinds = Arrays.asList(new String[] { "sign" });
        rightAssocs = Arrays.asList(new String[] { "=" });
        unaryOperators = Arrays.asList(new String[] { "+", "-", "!" });
        reserved = Arrays.asList(new String[] { "function", "return", "if", "else", "while", "break"});  // <-- Update
    }

Es ist eine Änderung des zu analysierenden Teils. Es wurde ein Aufruf für die Funktion hinzugefügt, die while zum ersten <-Add analysiert. Break Analyse zum zweiten <-Add hinzugefügt. Die Analyse von "break" erfolgt durch Zuweisen zu "token.kind", wobei die Bedeutung des Tokens für "brk" bestimmt wird.

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")) { // <-- Add
            return while_(token);
        } else if (token.kind.equals("ident") && token.value.equals("break")) { // <-- Add
            token.kind = "brk";
            return 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 {
            throw new Exception("The token cannot place there.");
        }
    }

Es ist eine Änderung des zu analysierenden Teils. Es wurde eine Methode while_ () hinzugefügt, die while-Anweisungen analysiert. Wie oben erwähnt, ist die Analyse der if-Anweisung eine einfache Erklärung.

Das Analyseergebnis der while-Anweisung wird im Argument-Token zusammengefasst.

Die Verarbeitung in der Methode "while_ ()" dient dazu, die Token in der while-Anweisungsdefinition der Reihe nach zu verfolgen. Erstens bestimmt die Zuweisung zu "token.kind" die Bedeutung des Tokens zu "while". Dann verbraucht es das ( am Anfang der bedingten Anweisung. Die Methode expression () analysiert die bedingte Anweisung der while-Anweisung und hält sie in token.left. Dann verbraucht es das Ende ) der bedingten Anweisung.

Analyse von Verarbeitungsblöcken. Wenn sich am Anfang des Blocks ein { befindet, ist es von einem { Token und einem } Token umgeben. Rufen Sie die body () Methode auf, die den Verarbeitungsblock analysiert, und halten Sie ihn in token.block. Wenn am Anfang des Blocks kein "{" steht, wird davon ausgegangen, dass es nur einen Verarbeitungsblock gibt, der in "token.block" gespeichert ist.

Parser.java


    private Token while_(Token token) throws Exception {
        token.kind = "while";
        consume("(");
        token.left = expression(0);
        consume(")");
        if (token().value.equals("{")) {
            token.block = body();
        } else {
            token.block = new ArrayList<Token>();
            token.block.add(expression(0));
        }
        return token;
    }

Interpreter.java

Eine Implementierung von Interpreter.java.

Dies ist eine Änderung der Methode body (), die Ausdrücke nacheinander ausführt.

Boolean [] brk zur Signatur von body () hinzugefügt. brk hat zwei Rollen. Das erste ist, dass, wenn "brk" nicht "null" ist, "break" möglich ist. Das zweite ist, dass wenn es "break" war, es auch an den Anrufer weitergegeben wird. Der Grund, brk zu einem Array-Typ von boolean zu machen, besteht darin, einen Wert an den Aufrufer zurückzugeben.

Bei "// <-Add 2" wird bestimmt, ob das nacheinander auszuführende Token "break" ist. Wenn der Token "break" ist, bestimmen Sie, ob es möglich ist, "break" zu machen. Ersetzen Sie "true", um dem Anrufer mitzuteilen, dass es "break" für "brk" war.

Es wurde ein Methodenaufruf zur Verarbeitung von while-Anweisungen zu // <-Add 1 hinzugefügt. Wenn also "return" im Verarbeitungsblock der while-Anweisung aufgerufen wird, kann es zur oberen Ebene zurückkehren. Bestimmt, ob "ret [0]" "true" ist, und bricht die "body ()" - Methode ab.

Interpreter.java


    public Object body(List<Token> body, boolean[] ret, boolean[] brk) throws Exception {
        for (Token exprs : body) {
            if (exprs.kind.equals("if")) {
                Object val = if_(exprs, ret, brk);
                if (ret != null && ret[0]) {
                    return val;
                }
            } else if (exprs.kind.equals("ret")) {
                if (ret == null) {
                    throw new Exception("Can not return");
                }
                ret[0] = true;
                if (exprs.left == null) {
                    return null;
                } else {
                    return expression(exprs.left);
                }
            } else if (exprs.kind.equals("while")) { // <-- Add 1
                Object val = while_(exprs, ret);
                if (ret != null && ret[0]) {
                    return val;
                }
            } else if (exprs.kind.equals("brk")) { // <-- Add 2
                if (brk == null) {
                    throw new Exception("Can not break");
                }
                brk[0] = true;
                return null;
            } else {
                expression(exprs);
            }
        }
        return null;
    }

Eine while_ () Methode, die eine while-Anweisung ausführt. token.left enthält den bedingten Ausdruck. Die Methode isTrue () bestimmt, ob der bedingte Ausdruck wahr ist. Wiederholen Sie token.block, während der bedingte Ausdruck wahr ist. Selbst wenn der bedingte Ausdruck wahr ist, im Methodenaufruf body () Wenn es sich um ein "Return" - oder "Break" -Token handelt, wird die Iteration unterbrochen.

Interpreter.java


    public Object while_(Token token, boolean[] ret) throws Exception {
        boolean[] brk = new boolean[1];
        Object val;
        while (isTrue(token.left)) {
            val = body(token.block, ret, brk);
            if (ret != null && ret[0]) {
                return val;
            }
            if (brk[0]) {
                return null;
            }
        }
        return null;
    }

Als nächstes folgt die Antwort auf das Ändern der Signatur der Methode "body ()".

Break kann auch in der if-Anweisung aufgerufen werden. Fügen Sie daher der Signatur der Methode if_ () ein formales Argument brk hinzu. Übergeben Sie es im Aufruf an die body () Methode.

Interpreter.java


    public Object if_(Token token, boolean[] ret, boolean[] brk) throws Exception {
        List<Token> block;
        if (isTrue(token.left)) {
            block = token.block;
        } else {
            block = token.blockOfElse;
        }
        if (block != null) {
            return body(block, ret, brk);
        } else {
            return null;
        }
    }

Dies ist eine Antwort auf die Änderung der Signatur der body () Methode.

Beim Aufruf der body () Methode in <-Update Weil es sich um einen Prozess in einem Kontext handelt, in dem der Aufruf "break" nicht möglich ist Das Argument brk der body () Methode wird mit null aufgerufen.

Interpreter.java


    public Map<String, Variable> run() throws Exception {
        body(body, null, null); // <-- Update
        return variables;
    }

    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;
                }
            }
            boolean[] ret = new boolean[1];
            return context.body(block, ret, null); // <-- Update
        }
    }

Das folgende Programm verwendet die obige Implementierung

v = 0
while (v < 4) {
  v = v + 1
  if (v == 2) {
    break
  }
}
println(v)

Drucken von "2" nacheinander auf die Standardausgabe.

Interpreter.java


    public static void main(String[] args) throws Exception {
        String text = "";
        text += "v = 0";
        text += "while (v < 4) {";
        text += "  v = v + 1";
        text += "  if (v == 2) {";
        text += "    break";
        text += "  }";
        text += "}";
        text += "println(v)";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> 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-12-while-r2/Calc/src/main/java

Es gibt einen Folgeartikel.

** Entspricht dem Umfang ** http://qiita.com/quwahara/items/d9f932195da1b655b617

Recommended Posts

12 Entspricht der while-Anweisung
10 Entspricht der if-Anweisung
Ist die While-Aussage nicht böse?
9 Entspricht dem Rückgabewert
17 Entspricht einem Array
Entspricht dem Geltungsbereich
Entspricht 15 Zeichenfolgen
[Java] für Anweisung, während Anweisung
8 Entspricht mehreren Argumenten
14 Entspricht einem Funktionsausdruck
5 Entspricht priorisierten Klammern
19 Entspricht der Objekterstellung
16 Entspricht dem Methodenaufruf
# 3 [Hinweis] do ~ while-Anweisung
Ich war süchtig danach, die Update-Anweisung in MyBatis zu wiederholen
18 Entspricht der JSON-ähnlichen Objektdefinition
Lassen Sie uns die if-Anweisung verstehen!
4 Fügen Sie dem Interpreter println hinzu
Lassen Sie uns die Wachaussage verstehen!
Die Idee von C # (Lambda-Ausdruck, für Satz) zu kauen
[Java] [jackson] Entspricht dem nachfolgenden Komma (nachgestellten Komma) beim Parsen von JSON.
20 Entspricht statischen Methodenaufrufen
Umgang mit dem Fehler FEHLER: Während der Ausführung von gem ... (Gem :: FilePermissionError)
Lassen Sie uns die For-In-Anweisung verstehen!
Lassen Sie uns die switch-Anweisung verstehen!
Ich möchte die if-else-Anweisung für bedingte Verzweigungen in Java vereinfachen
Eingabe in die Java-Konsole
So erhalten Sie den Inhalt von Map mithilfe des for-Anweisungsmemorandums
Verwendung der link_to-Methode
Fügen Sie der Datei erweiterte Attribute hinzu
Über die Sprache, die von nun an zu lernen ist
Verwendung der include? -Methode
11 Entspricht Vergleichs- und logischen Operatoren
Verwendung der Methode form_with
Übergeben Sie das Gebietsschema i18n an JavaScript
[java8] Um die Stream-API zu verstehen
So finden Sie den durchschnittlichen Winkel
Eine Geschichte, die ich mit der Stream-API von Java8 einem Prozess schreiben wollte, der einer while-Anweisung entspricht
Verwendung der Wrapper-Klasse
Java-Lernnotiz (while-Anweisung, do-while-Anweisung)
Ich habe versucht, die Methode zu erklären
Willkommen im Sumpf der Java-Bibliotheken! !!
So fügen Sie die Löschfunktion hinzu
Der Weg von JavaScript nach Java
[Einführung in Ruby] Über die Rolle von true und break in der while-Anweisung