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.
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 + "!")
Wir werden überlegen, wie es in der Reihenfolge der Phrasenanalyse (Lexer), Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.
Da es keine Funktion zum Parsen des in "
enthaltenen Zeichenfolgenliteral gibt, fügen Sie es hinzu.
Da String-Literale genauso behandelt werden können wie numerische Literale, werden Definitionen nach numerischen Literalen hinzugefügt.
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.
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.
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