[JAVA] Entspricht 15 Zeichenfolgen

Einführung

Im vorherigen Artikel Unterstützt Funktionsausdrücke. Bei der Entsprechung von Funktionsausdrücken ist der Typ, der als Wert behandelt wird, nur "Integer", und der Funktionsausdruck wird auch als Wert behandelt. Auf die gleiche Weise werden wir Zeichenfolgen als Werte behandeln.

Was Sie mit String-Unterstützung machen wollen

Bestätigen Sie, dass Sie Zeichenfolgen unterstützen möchten. Zum Beispiel gibt es das folgende Programm. Die in " eingeschlossene Zeichenfolge ist ein Zeichenfolgenliteral. Wenn eine Zeichenfolge unter Bedingungen wie einer if-Anweisung als logischer Wert behandelt wird, wird eine Zeichenfolge mit der Länge 0 als falsch angesehen. Ermöglicht die Verkettung von Zeichenfolgen mit "+". Der Methodenaufruf "println ()" am Ende des Programms zielt darauf ab, "Hallo Welt!" Auszugeben.

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

Wie zu implementieren

Wir werden überlegen, wie es in der Reihenfolge der Phrasenanalyse (Lexer), Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.

So implementieren Sie eine Phrasenanalyse (Lexer)

Da es keine Funktion zum Parsen des in " enthaltenen Zeichenfolgenliteral gibt, fügen Sie es hinzu.

So implementieren Sie Parser

Da String-Literale genauso behandelt werden können wie numerische Literale, werden Definitionen nach numerischen Literalen hinzugefügt.

So implementieren Sie Interpreter

Wir werden es möglich machen, "String" als Wert genauso zu behandeln wie "Integer". Die Verarbeitung von Einzelterm- und Binäroperatoren, die nur "Integer" berücksichtigten, Ändern Sie es so, dass es auch "String" verarbeiten kann.

Versuchen Sie, in Java zu implementieren

Fahren Sie mit der Implementierung fort. Informationen zur Phrasenanalyse (Lexer), Syntaxanalyse (Parser), Interpreter (Interpreter) Schauen wir uns die Änderungen und Ergänzungen der Reihe nach an.

Lexer.java

Eine Implementierung von Lexer.java.

Fügt die Möglichkeit hinzu, in " eingeschlossene Zeichenfolgen verbal zu analysieren.

Fügen Sie die Methode isStringStart () hinzu. " Erkennt den Start des Gehäuses.

Lexer.java


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

Fügen Sie die Methode string () hinzu. Analysiert bis zum Ende des Felds "" "zu einem einzelnen Token.

Implementieren Sie dies anhand der folgenden JSON-Zeichenfolge. http://json.org/json-ja.html Die Implementierung der Referenz mit 4 hexadezimalen Ziffern wurde abgeschnitten.

Die "Art", die ein String-Literal darstellt, sollte "String" sein.

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

Ändern Sie die Methode nextToken (). Fügen Sie den Aufruf der hinzugefügten Methode zu // Add hinzu. Sie können es jetzt in String-Literal-Token aufteilen.

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

Eine Implementierung von Parser.java.

Fügen Sie eine Definition hinzu, wie es für die Bedeutung des Tokens funktioniert. `` String "zu// Update` hinzugefügt. String-Literale, wie numerische Literale und Variablennamen, Da die Syntax nicht weiter zerlegt werden kann, wird sie der Faktorklassifizierung hinzugefügt. Die Syntaxanalyse des Faktors wurde bereits implementiert, daher ist dies das Ende der Parser-Implementierung.

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

Eine Implementierung von Interpreter.java.

Dies ist eine Modifikation der Methode expression (). Es ist ein Prozess, der nach der Bedeutung (Art) des Tokens verzweigt, das den Ausdruck darstellt. Unter // Add wurde ein Zweig für den Token string hinzugefügt, der ein String-Literal darstellt. Die Verarbeitung eines String-Tokens gibt einfach seinen Wert zurück.

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

Es ist eine Änderung der Methode value (). Die Methode value () gibt den Wert zurück, wenn das Argument der Wert selbst ist, z. B. 1. Wenn es sich um eine Variable handelt, wird der von der Variablen gehaltene Wert zurückgegeben. Es entsprach auch, wenn das Argument "String" war.

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

Dies ist eine Modifikation der Methode "integer ()". Die Methode "integer ()" garantiert, dass das Argument "Integer" ist. Geändert, um implizit zu versuchen, in "Integer" zu konvertieren, wenn das Argument "String" war.

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

Die Methode string () wurde hinzugefügt. Die Methode string () garantiert, dass das Argument String ist. Konvertieren Sie implizit in "String", wenn das Argument "Integer" ist.

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

Dies ist eine Modifikation der Methode "unaryCalc ()". Die Methode "unaryCalc ()" ist eine Methode, die mononomische Operatoren behandelt. Vor der Änderung wurde nur "Integer" unterstützt. Um String zu entsprechen, abhängig vom Ergebnis des Aufrufs der value () -Methode Wenn es sich um "Integer" handelt, rufen Sie die "unaryCalcInteger ()" -Methode auf, die den mononomischen Operator für "Integer" behandelt. Wenn es sich um "String" handelt, rufen Sie die Methode "unaryCalcString ()" auf, die den mononomischen Operator für "String" behandelt.

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

Dies ist eine Änderung der Methode calc (). Die calc () Methode ist eine Methode, die binäre Operatoren behandelt. Vor der Änderung wurde nur "Integer" unterstützt. Um String zu entsprechen, abhängig vom Ergebnis des Aufrufs der value () -Methode auf der linken Seite Wenn es sich um "Integer" handelt, rufen Sie die Methode "calcInteger ()" auf, die den Binäroperator für "Integer" behandelt. Wenn es sich um "String" handelt, rufen Sie die Methode "calcString ()" auf, die den Binäroperator für "String" behandelt. Die rechte Seite kann implizit konvertiert werden.

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

Dies ist eine Modifikation der Methode isTrue (). Geändert, so dass "String" als logischer Wert verarbeitet werden kann. Geändert, um eine Zeichenfolge der Länge 0 als falsch zu beurteilen.

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

Das folgende Programm verwendet die obige Implementierung

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

Und Der Methodenaufruf println () am Ende des Programms gibt Hello world! Aus.

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

Das ist alles für die Implementierung. Vielen Dank.

abschließend

Die vollständige Quelle finden Sie hier.

Calc https://github.com/quwahara/Calc/tree/article-15-string-r3/Calc/src/main/java

Es gibt einen Folgeartikel.

** Entspricht dem Methodenaufruf ** http://qiita.com/quwahara/items/f1bddefe984c8d233e02

Recommended Posts

Entspricht 15 Zeichenfolgen
17 Entspricht einem Array
Entspricht dem Geltungsbereich
8 Entspricht mehreren Argumenten
10 Entspricht der if-Anweisung
14 Entspricht einem Funktionsausdruck
5 Entspricht priorisierten Klammern
19 Entspricht der Objekterstellung
16 Entspricht dem Methodenaufruf
So verketten Sie Zeichenfolgen
9 Entspricht dem Rückgabewert
12 Entspricht der while-Anweisung
18 Entspricht der JSON-ähnlichen Objektdefinition
20 Entspricht statischen Methodenaufrufen
to_ ○
11 Entspricht Vergleichs- und logischen Operatoren
[Swift] So ersetzen Sie mehrere Zeichenfolgen
So verketten Sie Zeichenfolgen mit Java
Konvertieren Sie ein Array von Zeichenfolgen in Zahlen
Verein (1 zu 1)! !!
[java] Zusammenfassung des Umgangs mit Zeichenketten