[JAVA] 12 Correspond à l'instruction while

introduction

Dans les articles précédents, Comparaison et opérateurs logiques et Prend en charge les instructions if )Fait. Je voudrais soutenir la déclaration while comme une extension de cela.

Ce que vous voulez faire avec la prise en charge des instructions while

Assurez-vous de vouloir le faire facilement. Par exemple, si vous avez un programme comme celui ci-dessous Répétez avec l'instruction while, ignorez la répétition lorsque «v» devient «2» et visez à afficher «2» avec «println ()».

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

Comment mettre en œuvre

Comment mettre en œuvre La mise en œuvre de l'instruction while est presque la même que correspondance de l'instruction if. L'implémentation de l'instruction break est presque identique à Prise en charge de la valeur de retour de fonction. Veuillez donc vous y référer pour une explication détaillée et implémentons-le immédiatement.

Essayez de mettre en œuvre en Java

Passez à la mise en œuvre. À propos de l'analyseur syntaxique (Parser) et de l'interpréteur (Interpreter) Jetons un coup d'œil aux modifications et aux ajouts dans l'ordre.

Parser.java

Une implémentation de Parser.java. Ajoutez une définition de la façon dont cela fonctionne pour la signification du jeton. Puisque «while» et «break» sont des mots réservés, je les ai ajoutés à «<-Update».

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
    }

C'est un changement de la pièce à analyser. Ajout d'un appel à la fonction qui analyse while au premier <-Add. Ajout de l'analyse break au second <-Add. L'analyse de "break" se fait en attribuant à "token.kind", déterminant la signification du token à "brk".

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.");
        }
    }

C'est un changement de la pièce à analyser. Ajout d'une méthode while_ () qui analyse les instructions while. Comme mentionné ci-dessus, l'analyse de l'instruction if est une explication simple.

Le résultat de l'analyse de l'instruction while est résumé dans le jeton d'argument.

Le traitement dans la méthode while_ () est conçu pour tracer les jetons dans la définition de l'instruction while dans l'ordre. Tout d'abord, l'attribution à token.kind détermine la signification du jeton à while. Ensuite, il consomme le ( au début de l'instruction conditionnelle. La méthode ʻexpression () analyse l'instruction conditionnelle de l'instruction while et la conserve dans token.left. Ensuite, il consomme la fin )` de l'instruction conditionnelle.

Analyse des blocs de traitement. S'il y a un { au début du bloc, il est entouré d'un jeton { et d'un jeton }. Appelez la méthode body () qui analyse le bloc de traitement et maintenez-la dans token.block. S'il n'y a pas de { au début du bloc, on considère qu'il n'y a qu'un seul bloc de traitement et il est stocké dans token.block.

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

Une implémentation d'Interpreter.java.

Ceci est une modification de la méthode body () qui exécute les expressions séquentiellement.

Ajout de boolean [] brk à la signature de body (). brk a deux rôles. La première est que si «brk» n'est pas «null», alors «break» est possible. La seconde est que s'il s'agissait de "break", il sera également propagé à l'appelant. La raison pour laquelle «brk» est un tableau de type «booléen» est de renvoyer une valeur à l'appelant.

À «// <-Add 2», il est déterminé si le jeton à exécuter séquentiellement est «break». Si le jeton est «break», déterminez s'il est possible de «casser». Remplacez «true» pour dire à l'appelant que c'était «break» par «brk».

Ajout d'un appel de méthode pour traiter les instructions while à // <-Add 1. Ainsi, lorsque return est appelé dans le bloc de traitement de l'instruction while, il peut revenir au niveau supérieur. Détermine si ret [0] est true et abandonne la méthode body ().

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;
    }

Une méthode while_ () qui exécute une instruction while. token.left contient l'expression conditionnelle. La méthode ʻisTrue () détermine si l'expression conditionnelle est vraie. Répétez token.blocktant que l'expression conditionnelle est vraie. Cependant, même si l'expression conditionnelle est vraie, dans l'appel de la méthodebody ()` S'il s'agit d'un jeton «return» ou «break», l'itération sera interrompue.

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;
    }

Vient ensuite la réponse au changement de signature de la méthode body ().

Break peut également être appelé dans l'instruction if. Par conséquent, ajoutez un argument formel brk à la signature de la méthode ʻif_ (), Passez-le dans l'appel à la méthode body ()`.

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;
        }
    }

Ceci est une réponse au changement de signature de la méthode body ().

Dans l'appel à la méthode body () dans <-Update Parce que c'est un processus dans un contexte où l'appel break ne peut pas être effectué L'argument brk de la méthode body () est appelé avec null.

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
        }
    }

Le programme ci-dessous utilisant l'implémentation ci-dessus

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

Pour imprimer «2» sur la sortie standard en séquence.

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
    }

C'est tout pour la mise en œuvre. Merci beaucoup.

en conclusion

La source complète est disponible ici.

Calc https://github.com/quwahara/Calc/tree/article-12-while-r2/Calc/src/main/java

Il y a un article de suite.

** Correspond à la portée ** http://qiita.com/quwahara/items/d9f932195da1b655b617

Recommended Posts

12 Correspond à l'instruction while
10 Correspond à l'instruction if
La déclaration While n'est-elle pas mauvaise?
9 Correspond à la valeur de retour
17 Correspond à un tableau
Correspond à la portée
Correspond à 15 chaînes
[Java] pour instruction, while instruction
8 Correspond à plusieurs arguments
14 Correspond à une expression de fonction
5 Correspond aux parenthèses prioritaires
19 Correspond à la création d'objet
16 Correspond à l'invocation de méthode
# 3 [Note] instruction do ~ while
J'étais accro à la mise à jour de la déclaration dans MyBatis
18 Correspond à la définition d'objet de type JSON
Comprenons la déclaration if!
4 Ajoutez println à l'interpréteur
Comprenons la déclaration de garde!
L'idée de C # (expression lambda, pour phrase) à mâcher
[Java] [jackson] Correspond à la virgule de fin (virgule de fin) lors de l'analyse de JSON.
20 Correspond aux appels de méthode statiques
Comment gérer l'erreur ERREUR: lors de l'exécution de gem ... (Gem :: FilePermissionError)
Comprenons la déclaration for-in!
Comprenons l'instruction switch!
Je souhaite simplifier l'instruction if-else de la branche conditionnelle en Java
Entrée dans la console Java
Comment obtenir le contenu de la carte à l'aide du mémorandum d'instructions
Comment utiliser la méthode link_to
Ajouter des attributs étendus au fichier
À propos de la langue à apprendre à partir de maintenant
Comment utiliser la méthode include?
11 Correspond aux opérateurs de comparaison et logiques
Comment utiliser la méthode form_with
Passer les paramètres régionaux i18n à JavaScript
[java8] Pour comprendre l'API Stream
Comment trouver l'angle moyen
Je voulais écrire un processus équivalent à une instruction while avec l'API Java 8 Stream
Comment utiliser la classe wrapper
Mémo d'apprentissage Java (instruction while, instruction do-while)
J'ai essayé d'expliquer la méthode
L'histoire que je voulais développer Zip
Bienvenue dans le marais des bibliothèques Java! !!
Comment ajouter la fonction de suppression
La route de JavaScript à Java
[Introduction à Ruby] À propos du rôle de true et break in the while statement