[JAVA] 17 Correspond à un tableau

introduction

Je voudrais prendre en charge un tableau de structures de données de base. Cet article est une suite de "Correspondant aux appels de méthode".

Ce que vous voulez faire avec la prise en charge des baies

Confirmez ce que vous voulez faire avec la prise en charge de la baie. Par exemple, il existe le programme suivant. Créez un tableau sur la première ligne et affectez-le à la variable ʻar. La deuxième ligne affiche la longueur du tableau «3». La troisième ligne vise à afficher le troisième élément du tableau, " c "`.

var ar = ["a", "b", "c"]
println(ar.size())
println(ar[2])

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 l'analyse de phrases (Lexer)

Puisqu'il n'y a pas de fonction pour analyser [ et ], ajoutez-la.

Comment implémenter un analyseur

L'implémentation de l'analyse syntaxique correspond à deux syntaxes: l'une qui crée un tableau et l'autre qui accède aux éléments du tableau.

La syntaxe de génération d'un tableau commence par un jeton [. Implémentez-le dans la méthode lead (), tout comme les autres premières constructions de jetons.

La syntaxe pour accéder aux éléments d'un tableau est équivalente à la syntaxe d'un appel de fonction qui prend un argument. Le remplacement de «[]» de «ar [2]» par «()» dans l'exemple de programme entraîne «ar (2)». Vous pouvez voir qu'ils sont équivalents car ils ressemblent à des appels de fonction. Implémentez-le dans la méthode bind () de la même manière que l'analyse syntaxique des appels de fonction.

Comment implémenter Interpreter

Puisque l'entité du tableau peut ajouter des éléments et obtenir la longueur par la méthode d'instance, J'ai décidé d'utiliser ʻArrayList `. Dans l'article précédent, Prend en charge les appels de méthode, vous pouvez donc l'utiliser.

Le traitement de la syntaxe implémente la génération du tableau et l'accès aux éléments du tableau. Lors de la génération de tableaux, ʻArrayList est généré et des éléments sont ajoutés. Pour accéder à un élément de tableau, appelez la méthode ʻArrayList <Object> :: get () .

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.

Ajoutez la fonction d'analyse de phrase de [ et ].

Ajoutez la méthode ʻisBracketStart () `. Détecte «[» et «]».

Lexer.java


    private boolean isBracketStart(char c) {
        return c == '[' || c == ']';
    }

Ajoutez la méthode bracket (). Analyser [ et ] en jetons. kind, qui signifie [ et] , devrait être bracket.

Lexer.java


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

Modifiez la méthode nextToken (). Ajoutez l'appel à la méthode ajoutée à // Add. Vous pouvez maintenant décomposer [ et ] en jetons.

Lexer.java


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

C'est tout pour changer Lexer.java.

Parser.java

Une implémentation de Parser.java.

Ajoutez une définition de la façon dont cela fonctionne pour la signification du jeton. Ajout du degré de classement de [ to // Add. [ Est meilleur que les opérateurs arithmétiques comme + et * Parce qu'il relie fortement les jetons gauche et droit, Il est de 80, ce qui est plus grand que le degré de «+» et «*».

Parser.java


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

Implémente l'analyse syntaxique de génération de séquence. De la méthode lead () qui implémente la syntaxe déterminée par le premier jeton Ajout d'un appel à la méthode newArray () qui effectue une analyse de syntaxe de génération de tableau à // 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);
        } 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;
            // Add
        } else if (token.kind.equals("bracket") && token.value.equals("[")) {
            return newArray(token);
        } else {
            throw new Exception("The token cannot place there.");
        }
    }

Ajout de la méthode newArray () pour effectuer une analyse de la syntaxe de génération de tableaux. Rassemblez les éléments séparés par , dans les params du [token" jusqu'à ce que le token ] soit atteint. Le type de jeton «kind» est défini sur «newArray». Si le dernier élément du tableau est vide, comme [1,2,], il sera ignoré en tant qu'élément. Si un élément vide est spécifié au milieu de la séquence, comme «[1 ,, 3]» dans le tableau, Le jeton «vierge» préparé à l'avance est attribué.

Parser.java


    private Token newArray(Token token) throws Exception {
        token.kind = "newArray";
        token.params = new ArrayList<Token>();
        while(true) {
            if (token().value.equals("]")) {
                consume("]");
                break;
            }
            if (token().value.equals(",")) {
                token.params.add(blank);
                consume(",");
                continue;
            }
            token.params.add(expression(0));
            if (token().value.equals(",")) {
                consume(",");
                continue;
            } else {
                consume("]");
                break;
            }
        }
        return token;
    }

J'ai ajouté le jeton «vide» qui représente l'élément vide sorti plus tôt à la variable de champ statique.

Parser.java


    public static Token blank;
    static {
        blank = new Token();
        blank.kind = "blank";
        blank.value = "";
    }

Implémente la syntaxe d'accès au tableau. De la méthode bind () qui analyse la syntaxe d'appel de fonction, etc. Ajout de l'analyse de la syntaxe d'accès au tableau à // Add. Attribuez le tableau lui-même, par exemple, le jeton correspondant à ʻar de ʻar [2] à gauche de ʻopérateur correspondant au jeton [. Pour «droit» de «opérateur», attribuez l'index auquel accéder, par exemple le jeton correspondant à «2» de «ar [2]».

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(")")) {
                operator.params.add(expression(0));
                while (!token().value.equals(")")) {
                    consume(",");
                    operator.params.add(expression(0));
                }
            }
            consume(")");
            return operator;
            // Add
        } else if (operator.kind.equals("bracket") && operator.value.equals("[")) {
            operator.left = left;
            operator.right = expression(0);
            consume("]");
            return operator;
        } else {
            throw new Exception("The token cannot place there.");
        }
    }

C'est tout pour changer Parser.java.

Interpreter.java

Une implémentation d'Interpreter.java.

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. Sous «// Ajouter», Appelez la méthode blank ()pour les éléments vides, Appelez la méthodenewArray ()pour la génération de tableaux, Ajout de l'appel de la méthode ʻaccessArray ()pour l'accès au tableau.

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);
            // Add
        } else if (expr.kind.equals("blank")) {
            return blank(expr);
            // Add
        } else if (expr.kind.equals("newArray")) {
            return newArray(expr);
            // Add
        } else if (expr.kind.equals("bracket")) {
            return accessArray(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 d'une méthode blank () pour les éléments vides. Il renvoie simplement «null».

Interpreter.java


    public Object blank(Token token) {
        return null;
    }

Ajout de la méthode newArray () pour la génération de tableaux. Après avoir généré ʻArrayList ʻet l'ajout d'un élément, il le renvoie.

Interpreter.java


    public Object newArray(Token expr) throws Exception {
        List<Object> a = new ArrayList<>();
        for (Token item : expr.params) {
            a.add(value(expression(item)));
        }
        return a;
    }

Changement de la méthode value () de la méthode qui garantit que l'argument est une valeur. Le premier // Add autorise maintenant ʻArrayList comme valeur. Le prochain// Add autorise maintenant null` comme valeur.

Interpreter.java


    public Object value(Object value) throws Exception {
        if (value instanceof Integer) {
            return value;
        } else if (value instanceof String) {
            return value;
            // Add
        } else if (value instanceof List<?>) {
            return value;
            // Add
        } else if (value == null) {
            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");
    }

Ajout de la méthode ʻaccessArray () pour l'accès aux tableaux. L'argument ʻexpr reçoit un jeton [. Le [ token left résout le tableau lui-même. Le [ token right résout l'index d'accès. En les utilisant, utilisez la méthode ʻArrayList :: get () `pour extraire l'élément et le renvoyer comme valeur de retour.

Interpreter.java


    public Object accessArray(Token expr) throws Exception {
        List<Object> ar = array(expression(expr.left));
        Integer index = integer(expression(expr.right));
        return ar.get(index);
    }

Ajout de la méthode ʻarray () , une méthode qui garantit que l'argument est un tableau ( List ). Je garantirai que ce qui est accédé par la méthode ʻaccessArray () ci-dessus est un tableau.

Interpreter.java


    @SuppressWarnings("unchecked")
    public List<Object> array(Object value) throws Exception {
        if (value instanceof List<?>) {
            return (List<Object>) value;
        } else if (value instanceof Variable) {
            Variable v = (Variable) value;
            return array(v.value);
        }
        throw new Exception("right value error");
    }

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

var ar = ["a", "b", "c"]
println(ar.size())
println(ar[2])

Pour afficher la longueur du tableau "3" et le troisième élément du tableau "" c "".

Interpreter.java


    public static void main(String[] args) throws Exception {
        String text = "";
        text += "var ar = [\"a\", \"b\", \"c\"]";
        text += "println(ar.size())";
        text += "println(ar[2])";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> 3
        // --> c
    }

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-17-array/Calc/src/main/java

Il y a un article de suite.

** Correspond à la définition d'objet de type JSON ** http://qiita.com/quwahara/items/5e6cee17b7b03bd994ee

Recommended Posts