[JAVA] Correspond à 15 chaînes

introduction

Dans l'article précédent, Prend en charge les expressions de fonction. Avec la correspondance des expressions de fonction, le type traité comme une valeur est uniquement ʻInteger`, et l'expression de fonction est également traitée comme une valeur. De la même manière, nous traiterons les chaînes comme des valeurs.

Ce que vous voulez faire avec la prise en charge des chaînes

Confirmez que vous souhaitez prendre en charge les chaînes de caractères. Par exemple, il existe le programme suivant. La chaîne de caractères entourée de «» «est une chaîne de caractères littérale. Lorsqu'une chaîne de caractères est traitée comme une valeur logique dans des conditions telles qu'une instruction if, une chaîne de caractères de longueur 0 est considérée comme fausse. Permet aux chaînes d'être concaténées avec «+». L'appel de méthode println () à la fin du programme vise à afficher Hello world!.

var object = ""
if (!object) {
 object = "world"
}
println("Hello " + object + "!")

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 aucune fonction pour analyser la chaîne de caractères littérale entre «» », ajoutez-la.

Comment implémenter l'analyseur

Puisque les littéraux de chaîne peuvent être traités de la même manière que les littéraux numériques, nous ajouterons des définitions en fonction des littéraux numériques.

Comment implémenter Interpreter

Nous rendrons possible de gérer String comme valeur de la même manière que ʻInteger. Le traitement des opérateurs à un seul terme et binaires, qui ne considéraient que «Entier» Modifiez-le pour qu'il puisse également gérer String`.

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.

Ajoute la possibilité d'analyser une chaîne entre «» ».

Ajoutez la méthode ʻisStringStart () . " `Détecte le démarrage de l'enceinte.

Lexer.java


    private boolean isStringStart(char c) {
        return c == '"';
    }

Ajoutez la méthode string (). Analyse jusqu'à la fin de la boîte " en un seul jeton.

Implémentez en vous référant à la chaîne JSON ci-dessous. http://json.org/json-ja.html L'implémentation de référence à 4 chiffres hexadécimaux a été tronquée.

Le kind qui représente un littéral de chaîne doit être string.

Lexer.java


    private Token string() throws Exception {
        StringBuilder b = new StringBuilder();
        next();
        while (c() != '"') {
            if (c() != '\\') {
                b.append(next());
            } else {
                next();
                char c = c();
                if (c == '"') {
                    b.append('"');
                    next();
                } else if (c == '\\') {
                    b.append('\\');
                    next();
                } else if (c == '/') {
                    b.append('/');
                    next();
                } else if (c == 'b') {
                    b.append('\b');
                    next();
                } else if (c == 'f') {
                    b.append('\f');
                    next();
                } else if (c == 'n') {
                    b.append('\n');
                    next();
                } else if (c == 'r') {
                    b.append('\r');
                    next();
                } else if (c == 't') {
                    b.append('\t');
                    next();
                } else {
                    throw new Exception("string error");
                }
            }
        }
        next();
        Token t = new Token();
        t.kind = "string";
        t.value = b.toString();
        return t;
    }

Modifiez la méthode nextToken (). Ajoutez l'appel à la méthode ajoutée à // Add. Vous pouvez maintenant le décomposer en jetons littéraux de chaîne.

Lexer.java


    public Token nextToken() throws Exception {
        skipSpace();
        if (isEOT()) {
            return null;
        } else if (isSignStart(c())) {
            return sign();
        } else if (isDigitStart(c())) {
            return digit();
            // Add
        } else if (isStringStart(c())) {
            return string();
        } 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

Une implémentation de Parser.java.

Ajoutez une définition de la façon dont cela fonctionne pour la signification du jeton. Ajout de «string» à «// Update». Littéraux de chaîne, comme les littéraux numériques et les noms de variables, Comme la syntaxe ne peut pas être décomposée davantage, elle est ajoutée à la classification factorielle. L'analyse syntaxique de factor a déjà été implémentée, c'est donc la fin de l'implémentation de l'analyseur.

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);
        // Update
        factorKinds = Arrays.asList(new String[] { "digit", "ident", "string" });
        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", "var" });
    }

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. Ajout d'une branche sous // Add pour le token string`, qui représente une chaîne littérale. Le traitement d'un jeton «chaîne» renvoie simplement sa «valeur».

Interpreter.java


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

    public String string(Token token) {
        return token.value;
    }

C'est un changement de la méthode value (). 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. Cela correspondait également lorsque l'argument était «String».

Interpreter.java


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

Changement de méthode ʻInteger () . La méthode ʻinteger () garantit que l'argument est ʻInteger. Changé pour essayer implicitement de convertir en ʻInteger si l'argument était String.

Interpreter.java


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

Ajout de la méthode string (). La méthode string () garantit que l'argument est String. Convertit implicitement en String si l'argument est ʻInteger`.

Interpreter.java


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

Changement de méthode ʻUnaryCalc () . La méthode ʻunaryCalc () est une méthode qui gère les opérateurs mononomes. Avant le changement, seul ʻIntegerétait pris en charge. Pour correspondre àString, en fonction du résultat de l'appel de la méthode value () Si c'est ʻInteger, appelez la méthode ʻunaryCalcInteger () qui gère l'opérateur mononomial pour ʻInteger. S'il s'agit de String, appelez la méthode ʻunaryCalcString ()qui gère l'opérateur mononomial pourString`.

Interpreter.java


    public Object unaryCalc(Token expr) throws Exception {
        Object value = value(expression(expr.left));
        if (value instanceof Integer) {
            return unaryCalcInteger(expr.value, (Integer) value);
        } else if (value instanceof String) {
            return unaryCalcString(expr.value, (String) value);
        } else {
            throw new Exception("unaryCalc error");
        }
    }

    public Object unaryCalcInteger(String sign, Integer left) throws Exception {
        if (sign.equals("+")) {
            return left;
        } else if (sign.equals("-")) {
            return -left;
        } else if (sign.equals("!")) {
            return toInteger(!isTrue(left));
        } else {
            throw new Exception("unaryCalcInteger error");
        }
    }

    public Object unaryCalcString(String sign, String left) throws Exception {
        if (sign.equals("!")) {
            return toInteger(!isTrue(left));
        } else {
            throw new Exception("unaryCalcString error");
        }
    }

Ceci est un changement de la méthode calc (). La méthode calc () est une méthode qui gère les opérateurs binaires. Avant le changement, seul ʻIntegerétait pris en charge. Pour correspondre àString, en fonction du résultat de l'appel de la méthode value () sur le côté gauche Si c'est ʻInteger, appelez la méthodecalcInteger ()qui gère l'opérateur binaire pour ʻInteger`. S'il s'agit de «String», appelez la méthode «calcString ()» qui gère l'opérateur binaire pour «String». Le côté droit peut être converti implicitement.

Interpreter.java


    public Object calc(Token expr) throws Exception {
        Object left = value(expression(expr.left));
        Object right = value(expression(expr.right));
        Integer ileft = null;
        String sleft = null;

        if (left instanceof Integer) {
            ileft = (Integer) left;
        } else if (left instanceof String) {
            sleft = (String) left;
        }

        if (ileft != null) {
            return calcInteger(expr.value, ileft, right);
        } else if (sleft != null) {
            return calcString(expr.value, sleft, right);
        } else {
            throw new Exception("calc error");
        }
    }

    public Object calcInteger(String sign, Integer left, Object right) throws Exception {
        if (sign.equals("+")) {
            return left + integer(right);
        } else if (sign.equals("-")) {
            return left - integer(right);
        } else if (sign.equals("*")) {
            return left * integer(right);
        } else if (sign.equals("/")) {
            return left / integer(right);
        } else if (sign.equals("==")) {
            return toInteger(left == integer(right));
        } else if (sign.equals("!=")) {
            return toInteger(left != integer(right));
        } else if (sign.equals("<")) {
            return toInteger(left < integer(right));
        } else if (sign.equals("<=")) {
            return toInteger(left <= integer(right));
        } else if (sign.equals(">")) {
            return toInteger(left > integer(right));
        } else if (sign.equals(">=")) {
            return toInteger(left >= integer(right));
        } else if (sign.equals("&&")) {
            if (!isTrue(left)) {
                return left;
            }
            return right;
        } else if (sign.equals("||")) {
            if (isTrue(left)) {
                return left;
            }
            return right;
        } else {
            throw new Exception("calcIteger error");
        }
    }

    public Object calcString(String sign, String left, Object right) throws Exception {
        if (sign.equals("+")) {
            return left + string(right);
        } else if (sign.equals("==")) {
            return toInteger(left.equals(string(right)));
        } else if (sign.equals("!=")) {
            return toInteger(!left.equals(string(right)));
        } else if (sign.equals("<")) {
            return toInteger(left.compareTo(string(right)) < 0);
        } else if (sign.equals("<=")) {
            return toInteger(left.compareTo(string(right)) <= 0);
        } else if (sign.equals(">")) {
            return toInteger(left.compareTo(string(right)) > 0);
        } else if (sign.equals(">=")) {
            return toInteger(left.compareTo(string(right)) >= 0);
        } else if (sign.equals("&&")) {
            if (!isTrue(left)) {
                return left;
            }
            return right;
        } else if (sign.equals("||")) {
            if (isTrue(left)) {
                return left;
            }
            return right;
        } else {
            throw new Exception("calcString error");
        }
    }

Changement de méthode ʻIsTrue () . Modifié pour que String` puisse être traité comme une valeur logique. Changé pour juger une chaîne de caractères de longueur 0 comme fausse.

Interpreter.java


    public boolean isTrue(Object value) throws Exception {
        if (value instanceof Integer) {
            return 0 != ((Integer) value);
        } else if (value instanceof String) {
            return !"".equals(value);
        } else if (value instanceof Func) {
            return true;
        } else {
            return false;
        }
    }

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

var object = ""
if (!object) {
 object = "world"
}
println("Hello " + object + "!")

Et L'appel de la méthode println () à la fin du programme imprime Hello world!.

Interpreter.java


    public static void main(String[] args) throws Exception {
        String text = "";
        text += "var object = \"\"";
        text += "if (!object) {";
        text += " object = \"world\"";
        text += "}";
        text += "println(\"Hello \" + object + \"!\")";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> Hello world!
    }

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-15-string-r3/Calc/src/main/java

Il y a un article de suite.

** Correspond à l'invocation de méthode ** http://qiita.com/quwahara/items/f1bddefe984c8d233e02

Recommended Posts

Correspond à 15 chaînes
17 Correspond à un tableau
Correspond à la portée
8 Correspond à plusieurs arguments
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
Comment concaténer des chaînes
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
[Swift] Comment remplacer plusieurs chaînes
Comment concaténer des chaînes avec Java
Convertir un tableau de chaînes en nombres
Association (1 à 1)! !!
[java] Résumé de la gestion des chaînes de caractères