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.
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 + "!")
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).
Puisqu'il n'y a aucune fonction pour analyser la chaîne de caractères littérale entre «» », ajoutez-la.
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.
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`.
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 pour
String`.
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.
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