[JAVA] 8 Correspond à plusieurs arguments

introduction

Ajout de Définitions et appels de fonctions simples dans un article précédent. A cette époque, il n'y avait qu'un seul argument de fonction. Cette fois, il ne prend en charge aucun argument ou deux arguments ou plus.

Ce que vous voulez faire avec plusieurs arguments

Assurez-vous de vouloir le faire facilement. Par exemple, si vous avez un programme comme celui ci-dessous, la fonction ʻadd3 () est définie et nous visons à afficher 6` sur la sortie standard.

v = 0
function add3(a1, a2, a3) {
  v = a1 + a2 + a3
}
add3(1,2,3)
println(v)

C'est toujours dans l'article précédent qu'il ne correspond pas à la valeur de retour de la fonction ou à la portée de la variable.

Comment mettre en œuvre

Nous examinerons comment l'implémenter dans l'ordre de l'analyse des phrases (Lexer), de l'analyse syntaxique (Parser) et de l'interpréteur (Interpreter).

Comment implémenter dans l'analyse de phrase (Lexer)

Puisqu'il n'y a pas de fonction pour analyser les virgules et , dans la phrase d'analyse de l'implémentation jusqu'à présent, nous allons l'implémenter.

Comment implémenter un analyseur

Apportez des modifications à la partie implémentation de l'analyse syntaxique des définitions de fonction et des appels de fonction qui a été ajoutée dans l'article précédent. Les deux ne considèrent le cas qu'avec un seul argument, donc Modifiez l'implémentation de l'analyse pour ne prendre en compte aucun argument ou plusieurs arguments.

Comment implémenter Interpreter

C'est aussi une classe qui représente la fonction ajoutée dans l'article précédent, Utilisez-le pour apporter des modifications à la partie qui définit et appelle la fonction. Tous ne considèrent que le cas d'un seul argument, donc Modifiez pour ne prendre en compte aucun argument ou plusieurs arguments.

Essayez de mettre en œuvre en Java

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

Lexer.java

Une implémentation de Lexer.java. Tout d'abord, ajoutez la fonction pour analyser les virgules et ,.

Lexer.java


    private boolean isSymbolStart(char c) {
        return c == ',';
    }

    private Token symbol() throws Exception {
        Token t = new Token();
        t.kind = "symbol";
        t.value = Character.toString(next());
        return t;
    }

J'ajouterai la partie appel de l'analyse des virgules. L'implémentation de Lexer.java est terminée.

Lexer.java


    public Token nextToken() throws Exception {
        skipSpace();
        if (isEOT()) {
            return null;
        } else if (isSignStart(c())) {
            return sign();
        } else if (isDigitStart(c())) {
            return digit();
        } else if (isIdentStart(c())) {
            return ident();
        } else if (isParenStart(c())) {
            return paren();
        } else if (isCurlyStart(c())) {
            return curly();
        } else if (isSymbolStart(c())) {
            return symbol();
        } else {
            throw new Exception("Not a character for tokens");
        }
    }

Parser.java

Changement de la méthode func () pour analyser la définition de la fonction. Avant d'expliquer le changement de méthode func (), car le changement de classe Token est pertinent Je vais vous expliquer comment changer la classe Token. Le résultat de l'analyse de la définition de fonction est résumé dans l'argument token. Dans l'article précédent, j'ai ajouté une variable de champ appelée param qui représente un argument formel de la classe Token. Cette fois, afin de prendre en charge plusieurs arguments, le champ param est supprimé et une variable de champ appelée params de typeList <Token>est ajoutée à la classe Token. Vient ensuite l'explication de la modification de la méthode func (). Les principaux changements sont «<-Update». L'analyse syntaxique y est la suivante: (Après le jeton, après

Et analysez.

Parser.java


    private Token func(Token token) throws Exception {
        token.kind = "func";
        token.ident = ident();
        consume("(");
        token.params = new ArrayList<Token>();
        if (!token().value.equals(")")) {           // <-- Update
            token.params.add(ident());
            while (!token().value.equals(")")) {
                consume(",");
                token.params.add(ident());
            }
        }
        consume(")");
        consume("{");
        token.block = block();
        consume("}");
        return token;
    }

Une implémentation de l'analyse de syntaxe pour la partie qui effectue l'appel de fonction. Le principal changement est <-Update, et vous pouvez voir que la méthode d'implémentation est presque la même que l'analyse syntaxique de la définition de fonction précédente.

Parser.java


    private Token bind(Token left, Token operator) throws Exception {
        if (binaryKinds.contains(operator.kind)) {
            operator.left = left;
            int leftDegree = degree(operator);
            if (rightAssocs.contains(operator.value)) {
                leftDegree -= 1;
            }
            operator.right = expression(leftDegree);
            return operator;
        } else if (operator.kind.equals("paren") && operator.value.equals("(")) {
            operator.left = left;
            operator.params = new ArrayList<Token>();
            if (!token().value.equals(")")) {           // <-- Update
                operator.params.add(expression(0));
                while (!token().value.equals(")")) {
                    consume(",");
                    operator.params.add(expression(0));
                }
            }
            consume(")");
            return operator;
        } else {
            throw new Exception("The token cannot place there.");
        }
    }

Interpreter.java

Une implémentation d'Interpreter.java.

Une modification de la classe abstraite de la classe qui représente la fonction. Argument de méthode ʻInvoke () selon plusieurs arguments, Changé de ʻObject arg en List <Object> args.

Interpreter.java


    public static abstract class Func {
        public String name;

        abstract public Object invoke(List<Object> args) throws Exception;
    }

J'ai changé la méthode ʻinvoke () de la classe Println` pour correspondre au changement de la classe abstraite.

Interpreter.java


    public static class Println extends Func {
        public Println() {
            name = "println";
        }

        @Override
        public Object invoke(List<Object> args) throws Exception {
            Object arg = args.size() > 0 ? args.get(0) : null;
            System.out.println(arg);
            return null;
        }
    }

De même, la méthode ʻinvoke () de la classe DynamicFunc` a été modifiée pour correspondre au changement de la classe abstraite. Changer. De plus, la variable de champ «param» a été supprimée afin que plusieurs arguments puissent être exprimés, et «params» de type «List » a été introduit.

Interpreter.java


    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;
                }
            }
            context.body(block);
            return null;
        }
    }

C'est un changement de la partie définition de la fonction. Les principaux changements sont «<-Add» et «<-Update». Il est répété dans l'instruction for pour traiter plusieurs arguments.

Interpreter.java


    public Object func(Token token) throws Exception {
        String name = token.ident.value;
        if (functions.containsKey(name)) {
            throw new Exception("Name was used");
        }
        if (variables.containsKey(name)) {
            throw new Exception("Name was used");
        }
        List<String> paramCheckList = new ArrayList<String>(); // <-- Add
        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 = this;
        func.name = name;
        func.params = token.params; // <-- Update
        func.block = token.block;
        functions.put(name, func);
        return null;
    }

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

v = 0
function add3(a1, a2, a3) {
  v = a1 + a2 + a3
}
add3(1,2,3)
println(v)

Pour imprimer la valeur «6» affectée à la variable «v» sur la sortie standard.

Interpreter.java


    public static void main(String[] args) throws Exception {
        String text = "";
        text += "v = 0";
        text += "function add3(a1, a2, a3) {";
        text += "  v = a1 + a2 + a3";
        text += "}";
        text += "add3(1,2,3)";
        text += "println(v)";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> 6
    }

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-8-multiple-arguments-r2/Calc/src/main/java

Il y a un article de suite.

** Correspond à la valeur de retour ** http://qiita.com/quwahara/items/1db9a5b880fd36dcfd3c

Recommended Posts

8 Correspond à plusieurs arguments
17 Correspond à un tableau
Correspond à la portée
Correspond à 15 chaînes
10 Correspond à l'instruction if
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
9 Correspond à la valeur de retour
12 Correspond à l'instruction while
18 Correspond à la définition d'objet de type JSON
20 Correspond aux appels de méthode statiques
11 Correspond aux opérateurs de comparaison et logiques
Opération pour connecter plusieurs Streams @Java
[Swift] Comment remplacer plusieurs chaînes
à_ ○
Mémo pour avoir plusieurs valeurs dans HashMap
Résumé de l'écriture des arguments d'annotation
Comment effectuer un micro-bench tout en modifiant les arguments de VM
Comment basculer entre plusieurs versions de Java