[JAVA] 19 Correspond à la création d'objet

introduction

Je veux charger une classe Java et appeler un constructeur pour gérer la création d'objets. Cet article est une suite de "Correspondant aux définitions d'objets de type JSON".

Ce que vous voulez faire avec la prise en charge de la création d'objets

Confirmez ce que vous voulez faire avec la prise en charge de la création d'objets. Par exemple, il existe le programme suivant. Introduit la fonction loadClass comme fonction globale par défaut. La fonction loadClass est une chaîne comme argument, et si vous passez un nom de classe Java complet, cette classe sera chargée. (La première ligne) Celui chargé est affecté à la variable. Si vous ajoutez new à cette variable et que vous l'appelez comme une fonction, cela créera une instance. (2e ligne)

var dateClass = loadClass("java.util.Date")
var date = new dateClass()
println(date.toString())

Comment mettre en œuvre

Nous examinerons comment l'implémenter dans l'ordre de l'analyse syntaxique (Parser) et de l'interpréteur (Interpreter). Il n'y a pas de changement dans l'analyse des phrases (Lexer).

Comment implémenter un analyseur

La syntaxe nouvellement ajoutée est «new», alors réfléchissons à la façon d'analyser «new». new vient en premier dans la syntaxe, donc comme tout autre jeton dans la syntaxe. Implémentez le processus d'analyse de la syntaxe de la méthode new dans la méthode lead (). Puisque l'élément de la syntaxe qui vient après new a la même syntaxe que l'appel de fonction, Le processus d'analyse de la syntaxe de «nouveau» vérifie que l'élément suivant est un appel de fonction.

Comment implémenter Interpreter

L'interpréteur implémente la fonction nouvellement introduite «loadClass» et l'exécution de la «nouvelle» syntaxe.

Implémentez la fonction loadClass de la même manière que la fonction globale par défaut existante println.

L'implémentation de l'exécution de la syntaxe new est la syntaxe qui vient après new Traitez-le comme un appel de fonction et faites-le instancier.

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.

Implémente l'analyse syntaxique new. De la méthode lead () qui implémente la syntaxe déterminée par le premier jeton Ajout d'un appel à la méthode new_ () qui analyse new to // Add.

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

Ajout de la méthode new_ () pour effectuer l'analyse syntaxique new. L'argument «token» reçoit un «nouveau» token. Le type de jeton new, kind, qui est la clé du traitement de l'interpréteur, est défini sur new. La «gauche» du «nouveau» jeton attribue la valeur de retour de la méthode «expression». Assurez-vous que la valeur de retour est une analyse syntaxique de l'appel de fonction.

Parser.java


    private Token new_(Token token) throws Exception {
        token.kind = "new";
        token.left = expression(0);
        if (!(token.left.value.equals("(") && token.left.kind.equals("paren"))) {
            throw new Exception("No constructor invocation.");
        }
        return token;
    }

C'est tout pour changer Parser.java.

Interpreter.java

Une implémentation d'Interpreter.java.

Ajout d'une classe qui est la substance de la fonction loadClass. L'argument de la méthode ʻinvoke correspond à l'argument de l'appel de fonction loadClass. En supposant que le premier argument est une chaîne de noms de classe pleinement qualifiés Chargez la classe à l'aide de la méthode Class.forName`.

Interpreter.java


    public static class LoadClass extends Func {
        public LoadClass() {
            name = "loadClass";
        }

        @Override
        public Object invoke(List<Object> args) throws Exception {
            return Class.forName((String) args.get(0));
        }
    }

Changement de méthode ʻInit () . Une méthode qui initialise l'interpréteur. Instanciez la classe LoadClass ajoutée à // Addet Pour qu'elle puisse être appelée comme fonction globale par défaut Ajouter auxfonctions` dans la portée globale.

Interpreter.java


    public Interpreter init(List<Token> body) {
        global = new Scope();
        local = global;
        // Add
        Func loadClass = new LoadClass();
        global.functions.put(loadClass.name, loadClass);
        Func f = new Println();
        global.functions.put(f.name, f);
        this.body = body;
        return this;
    }

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 de l'appel de méthode new_ ()pour la création d'objets sous// Add`.

Interpreter.java


    public Object expression(Token expr) throws Exception {
        if (expr.kind.equals("digit")) {
            return digit(expr);
        } else if (expr.kind.equals("string")) {
            return string(expr);
        } else if (expr.kind.equals("ident")) {
            return ident(expr);
        } else if (expr.kind.equals("blank")) {
            return blank(expr);
            // Add
        } else if (expr.kind.equals("new")) {
            return new_(expr);
        } else if (expr.kind.equals("newMap")) {
            return newMap(expr);
        } else if (expr.kind.equals("newArray")) {
            return newArray(expr);
        } else if (expr.kind.equals("bracket")) {
            return accessArrayOrMap(expr);
        } else if (expr.kind.equals("func")) {
            return func(expr);
        } 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 if (expr.kind.equals("dot")) {
            return dot(expr);
        } else {
            throw new Exception("Expression error");
        }
    }

Ajout de la méthode new_ () pour la création d'objets. Cette méthode appelle le constructeur et retourne l'objet créé.

Le jeton new est passé à l'argument de méthode new_ () ʻexpr. La «gauche» du «nouveau» jeton se voit attribuer un jeton qui a la syntaxe d'appel de fonction. La «gauche» du «nouveau» jeton, qui est la syntaxe de l'appel de fonction, et la «gauche» sont la partie correspondant au nom de la fonction. Le params de la gauche du jeton new` est l'argument de la syntaxe d'appel de fonction.

La partie correspondant au nom de la fonction (ʻexpr.left.left`) est une classe dans l'appel du constructeur, donc Au début du processus, cette partie est résolue en classe.

Pour effectuer un appel de constructeur, vous devez obtenir des informations qui représentent le constructeur. Les informations sur le constructeur peuvent être obtenues à partir des informations qui représentent la classe. La variable c résolue en classe est l'information pour cette classe.

Vous pouvez obtenir les informations du constructeur à partir des informations de classe, Parmi les multiples constructeurs définis dans la classe Vous devez sélectionner une information sur le constructeur que vous souhaitez appeler.

Dans la méthode new_, afin de sélectionner une information de constructeur que vous voulez appeler Filtrez en utilisant les informations de type de l'argument de l'appel du constructeur.

Si les informations de constructeur correspondantes sont introuvables ou réduites à une, une erreur se produit.

La méthode de réduction est très simple et est différente de l'implémentation dans le compilateur Java. J'ai fait un commentaire avec une petite explication détaillée.

Interpreter.java


    public Object new_(Token expr) throws Exception {
        
        // "("Résolvez le côté gauche de dans une classe et obtenez une liste de constructeurs pour cette classe
        Class<?> c = class_(expression(expr.left.left));
        Constructor<?>[] ctors = c.getConstructors();
        
        // "("Rendre l'argument constructeur sur le côté droit de la liste résolu en valeur
        List<Object> args = new ArrayList<Object>();
        for (Token arg : expr.left.params) {
            args.add(value(expression(arg)));
        }

        //Faire une liste des types d'arguments à partir des arguments
        List<Class<?>> aClasses = argClasses(args);

        //La liste des types d'arguments est limitée aux constructeurs dont les signatures peuvent être attribuées.
        List<Constructor<?>> byAssignables = ctorsByAssignable(ctors, aClasses);

        //En raison de la réduction, une erreur se produit s'il n'y a pas de constructeur correspondant
        if (byAssignables.size() == 0) {
            throw new Exception("No constructor error");
        }

        Constructor<?> ctor;
        if (byAssignables.size() == 1) {

            //En raison de la réduction, s'il n'y a qu'un seul constructeur applicable,
            //C'est le constructeur à appeler
            ctor = byAssignables.get(0);

        } else {

            //En raison de la réduction, s'il y a deux ou plusieurs constructeurs applicables, rétrécissez davantage.
            //Une liste de constructeurs restreinte par des signatures assignables,
            //Limitez-vous aux seuls constructeurs qui ont une signature qui correspond exactement à la liste des types d'arguments.
            List<Constructor<?>> byAbsolutes = ctorsByAbsolute(byAssignables, aClasses);

            //En raison de la réduction, une erreur se produira si le nombre de constructeurs applicables n'est pas un.
            if (byAbsolutes.size() != 1) {
                throw new Exception("No constructor error");
            }

            //En raison de la réduction, s'il n'y a qu'un seul constructeur applicable,
            //C'est le constructeur à appeler
            ctor = byAbsolutes.get(0);

        }

        //Faire un appel au constructeur en utilisant un seul constructeur
        Object val = ctor.newInstance(args.toArray());
        return val;
    }

    public List<Class<?>> argClasses(List<Object> args) {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        int psize = args.size();
        for (int i = 0; i < psize; ++i) {
            Object a = args.get(i);
            if (a != null) {
                classes.add(a.getClass());
            } else {
                classes.add(null);
            }
        }
        return classes;
    }

    public static List<Constructor<?>> ctorsByAssignable(Constructor<?>[] ctors, List<Class<?>> aClasses) {
        List<Constructor<?>> candidates = new ArrayList<Constructor<?>>();

        int aSize = aClasses.size();
        for (Constructor<?> ctor : ctors) {
            Class<?>[] pTypes = ctor.getParameterTypes();

            if (pTypes.length != aSize) {
                continue;
            }

            Boolean allAssignable = true;
            for (int i = 0; i < aSize; ++i) {
                Class<?> c = pTypes[i];
                Class<?> cc = toBoxClass(c);
                Class<?> ac = aClasses.get(i);
                if (ac != null) {
                    Class<?> acc = toBoxClass(ac);
                    allAssignable &= cc.isAssignableFrom(acc);
                }
                if (!allAssignable) {
                    break;
                }
            }
            if (allAssignable) {
                candidates.add(ctor);
            }
        }
        return candidates;
    }

    public static List<Constructor<?>> ctorsByAbsolute(List<Constructor<?>> candidates, List<Class<?>> aClasses) {
        List<Constructor<?>> screened = new ArrayList<Constructor<?>>();
        int aSize = aClasses.size();
        for (int i = 0; i < aSize; ++i) {
            Class<?> ac = aClasses.get(i);
            if (ac == null) {
                return screened;
            }
        }
        for (Constructor<?> ctor : candidates) {
            Class<?>[] pTypes = ctor.getParameterTypes();
            if (aSize != pTypes.length) {
                continue;
            }
            Boolean allEquals = true;
            for (int i = 0; i < aSize; ++i) {
                Class<?> c = pTypes[i];
                Class<?> ac = aClasses.get(i);
                allEquals &= c == ac;
                if (!allEquals) {
                    break;
                }
            }
            if (allEquals) {
                screened.add(ctor);
            }
        }
        return screened;
    }

Ajout d'une méthode class_ () qui garantit que la valeur de l'argument est une classe. Ueno Utilisé au début de la méthode new ().

Interpreter.java


    public Class<?> class_(Object value) throws Exception {
        if (value instanceof Class<?>) {
            return (Class<?>) value;
        } else if (value instanceof Variable) {
            Variable v = (Variable) value;
            return class_(v.value);
        }
        throw new Exception("right value error");
    }

Changement de la méthode value () de la méthode qui garantit que l'argument est une valeur. Jusqu'à présent, la politique de mise en œuvre consistait à vérifier si le type de l'argument «valeur» était valide en tant que valeur. Maintenant que vous pouvez créer des objets, vous ne pouvez plus examiner tous les types de valeur pour déterminer s'ils sont valides. Par conséquent, j'ai ajouté void_ à la variable de champ statique, ce qui indique qu'elle est inappropriée en tant que valeur. Si l'argument «valeur» est «void_», il est considéré comme inapproprié en tant que valeur.

Interpreter.java



    Scope global;
    Scope local;
    List<Token> body;
    // Add
    public static Object void_ = new Object();

    public Object value(Object value) throws Exception {
        if (value == void_) {
            throw new Exception("right value error");
        } else if (value instanceof Variable) {
            Variable v = (Variable) value;
            return value(v.value);
        }
        return value;
    }

Correspondant à l'ajout de void_, qui indique que la valeur est inappropriée, Changé pour retourner void_ au lieu de renvoyer null pour ceux qui n'ont pas renvoyé de valeur jusqu'à présent. Modifié pour renvoyer void_ là où il y a // Update dans chaque méthode ci-dessous.

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) {
                    // Update
                    return void_;
                } else {
                    return expression(exprs.left);
                }
            } else if (exprs.kind.equals("while")) {
                Object val = while_(exprs, ret);
                if (ret != null && ret[0]) {
                    return val;
                }
            } else if (exprs.kind.equals("brk")) {
                if (brk == null) {
                    throw new Exception("Can not break");
                }
                brk[0] = true;
                // Update
                return void_;
            } else if (exprs.kind.equals("var")) {
                var(exprs);
            } else {
                expression(exprs);
            }
        }
        // Update
        return void_;
    }

    public Object ret(Token token) throws Exception {
        if (token.left == null) {
            // Update
            return void_;
        }
        return expression(token.left);
    }

    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 {
            // Update
            return void_;
        }
    }

    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]) {
                // Update
                return void_;
            }
        }
        // Update
        return void_;
    }

    public Object var(Token token) throws Exception {
        for (Token item : token.block) {
            String name;
            Token expr;
            if (item.kind.equals("ident")) {
                name = item.value;
                expr = null;
            } else if (item.kind.equals("sign") && item.value.equals("=")) {
                name = item.left.value;
                expr = item;
            } else {
                throw new Exception("var error");
            }
            if (!local.variables.containsKey(name)) {
                newVariable(name);
            }
            if (expr != null) {
                expression(expr);
            }
        }
        // Update
        return void_;
    }

    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);
            // Update
            return void_;
        }
    }

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

var dateClass = loadClass("java.util.Date")
var date = new dateClass()
println(date.toString())

Pour générer un objet Date et afficher la date et l'heure d'exécution.

Interpreter.java


    public static void main(String[] args) throws Exception {
        String text = "";
        text += "var dateClass = loadClass(\"java.util.Date\")";
        text += "var date = new dateClass()";
        text += "println(date.toString())";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> Sat Jun 17 18:29:13 JST 2017 (Date et heure d'exécution)
    }

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-19-class-loading/Calc/src/main/java

Il y a un article de suite.

** Correspond aux appels de méthode statiques ** http://qiita.com/quwahara/items/a8ef47f78b1479a117de

Recommended Posts

19 Correspond à la création d'objet
18 Correspond à la définition d'objet de type JSON
17 Correspond à un tableau
Correspond à la portée
Correspond à 15 chaînes
8 Correspond à plusieurs arguments
Road to REPL (?) Création (3)
10 Correspond à l'instruction if
14 Correspond à une expression de fonction
5 Correspond aux parenthèses prioritaires
Road to REPL (?) Création (1)
16 Correspond à l'invocation de méthode
Road to REPL (?) Création (2)
12 Correspond à l'instruction while
Convertir un objet sérialisable en octet []
20 Correspond aux appels de méthode statiques
11 Correspond aux opérateurs de comparaison et logiques
Convertir un objet ruby au format JSON
à_ ○
Essayez d'organiser votre propre orientation d'objet
La voie de la création de services Web (partie 2)
Mappage de json sur un objet déjà instancié
Introduction à kotlin pour les développeurs iOS ⑥ ー création de kotlin