[JAVA] 14 Correspond à une expression de fonction

introduction

Dans l'article précédent, Supports Scope. Cette fois, cela correspond à l'expression de la fonction. Je vais essayer de faire une fermeture en utilisant la portée qui a été prise en charge dans l'article précédent.

Ce que vous voulez faire avec la prise en charge des expressions de fonction

Confirmez ce que vous voulez faire avec la prise en charge des expressions de fonction. Par exemple, il existe le programme suivant. L'expression de fonction interne renvoyée par l'expression de fonction externe est affectée à la variable «counter». Chaque fois que vous appelez counter en tant que fonction La valeur de la variable «c» définie dans l'expression de la fonction externe est ajoutée. Le premier println (counter ()) imprime 1, Le prochain println (counter ()) affichera 2.

counter = (function() {
  var c = 0
  return function() {
    c = c + 1
    return c
  }
})()
println(counter())
println(counter())

Comment mettre en œuvre

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

Comment implémenter l'analyseur

L'analyse syntaxique d'une expression de fonction est différente de l'analyse syntaxique d'une définition de fonction avec ou sans nom de fonction. La différence se reflète dans l'analyse syntaxique de la définition de fonction.

Comment implémenter Interpreter

Introduit un objet qui représente une expression de fonction. L'objet peut être traité comme une valeur et il prend également en charge les appels de fonction. Jusqu'à présent, seul ʻInteger` était traité comme une valeur, Les expressions de fonction doivent également être traitées comme des valeurs.

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.

Une modification de la méthode func () qui analyse la définition de la fonction. La partie modifiée est la première instruction if. L'implémentation d'origine s'attendait à ce que le jeton «fonction» soit suivi du jeton «ident », qui est le nom de la fonction. Dans le cas d'une expression de fonction, le jeton fonction est suivi du jeton (. L'instruction if est jugée après que le jeton fonction est (s'il s'agit d'un jeton, l'expression de la fonction est analysée, Sinon, je l'ai changé pour analyser la définition de la fonction.

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

Une implémentation d'Interpreter.java.

Une modification de la classe «Variable» qui représente une variable. Jusqu'à présent, la valeur ne traitait que de ʻInteger. Puisque nous introduisons une expression de fonction, l'expression de fonction doit également être traitée comme une valeur. Type de variable de champ valeur à gérer, Changé de ʻInteger en ʻObject`.

Interpreter.java


    public static class Variable {
        public String name;
        public Object value;

        @Override
        public String toString() {
            return name + " " + value;
        }
    }

Modification de la méthode ʻExpression () . C'est un processus qui se ramifie en fonction de la signification (sorte) du jeton qui représente l'expression. Ajout d'une branche sous // Add pour le jeton fexpr`, qui représente une expression de fonction.

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

Ajout de la méthode fexpr (), qui est appelée sur la branche ajoutée par la méthode ʻexpression ci-dessus. Crée un objet qui représente une expression de fonction. Pour pouvoir maintenir la portée parente de l'expression de fonction, étant donné que l'expression de fonction est utilisée dans la fermeture Clonez et conservez le ʻInterpreter actuel dans func.context.

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

Une modification de la méthode func () qui crée une instance qui représente une définition de fonction. Synchronisé avec la méthode fexpr () clonant le ʻInterpreteractuel, Je l'ai également changé pour cloner sous// Update`.

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

Changez la méthode value () et ajoutez la méthode ʻinteger () `.

La méthode value () renvoie la valeur si l'argument est la valeur elle-même, telle que "1". S'il s'agit d'une variable, elle renvoie la valeur détenue par la variable. Jusqu'à présent, nous n'avons traité de ʻIntegerque pour les valeurs. L'expression de fonction correspondante est également la valeur renvoyée par cette méthode, elle correspond donc. Changez le type de retour de la méthodevalue () de ʻInteger en ʻObject. Vous pouvez maintenant renvoyer à la fois ʻInteger et l'expression de fonction avec la méthodevalue ().

La raison de l'ajout de la méthode ʻinteger () est Cela est dû au fait que la valeur de retour de la méthode value () a été modifiée de ʻInteger en ʻObject. Jusqu'à présent, la valeur de retour de la méthode value () était garantie comme ʻInteger, Le changement de ʻInteger en ʻObject ne garantit plus. Au lieu de cela, la méthode ʻinteger () garantit que la valeur est ʻInteger.

Pour influencer ces changements et s'assurer que la valeur est ʻInteger La méthodevalue ()a été appelée, Même avec la méthode ʻunaryCalc ()et la méthodecalc () Je change pour appeler la méthode ʻinteger () `à la place.

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

Ceci est une modification de la méthode func (). Une méthode qui garantit que l'argument est une fonction. Pour ʻelse if, si l'argument est de type Variable` Traitement supplémentaire pour s'assurer que la valeur est une fonction.

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

Changement de méthode «Est vrai». La méthode ʻisTrue est le processus de résolution d'une valeur en une valeur booléenne. Jusqu'à présent, la valeur était uniquement ʻInteger, mais comme les fonctions sont désormais traitées comme des valeurs, Lorsque la valeur était une fonction, elle se résolvait simplement en «vrai».

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

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

counter = (function() {
  var c = 0
  return function() {
    c = c + 1
    return c
  }
})()
println(counter())
println(counter())

Et Le premier println (counter ()) imprime 1, Le prochain println (counter ()) affichera 2.

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
    }

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-14-function-expression-r3/Calc/src/main/java

Il y a un article de suite.

** Correspond à une chaîne de caractères ** http://qiita.com/quwahara/items/ddcbc8c37b9d442fe0f2

Recommended Posts

14 Correspond à une expression de fonction
17 Correspond à un tableau
Correspond à la portée
Correspond à 15 chaînes
8 Correspond à plusieurs arguments
10 Correspond à l'instruction if
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
Java pour jouer avec Function
Comment ajouter la fonction ActionText
12 Correspond à l'instruction while
[Java] Introduction à l'expression lambda
11 Correspond aux opérateurs de comparaison et logiques
[Introduction à Java] À propos des expressions lambda
La fonction est très facile à utiliser
Essayez d'implémenter la fonction Widget iOS14
Comment ajouter la fonction de suppression
Points à noter dans les expressions conditionnelles
Comment utiliser les expressions Java lambda
à_ ○
J'ai essayé de résumer les expressions Java lambda
Essayez d'implémenter une fonction de connexion avec Spring-Boot
Comment implémenter TextInputLayout avec la fonction de validation
[Traitement × Java] Comment utiliser la fonction
Facile à parcourir avec les expressions régulières Java