Correspondant à la valeur de retour de la fonction dans l'article précédent. Ensuite, je voudrais soutenir les déclarations if.
Assurez-vous de vouloir le faire facilement.
Par exemple, si vous avez un programme comme celui ci-dessous, le premier println ()
affichera 3
.
Le prochain println ()
vise à imprimer 4
.
Pour des raisons de simplicité, l'authenticité de l'instruction if est déterminée en supposant que non nul est vrai et 0 est faux.
function f(a) {
if (a) {
return 3
} else {
return 4
}
}
println(f(1))
println(f(0))
Nous examinerons comment l'implémenter dans l'ordre de l'analyse syntaxique (Parser) et de l'interpréteur (Interpreter). L'analyse de phrase (Lexer) n'est pas modifiée.
Pour savoir comment implémenter l'analyse syntaxique de l'instruction if, voir [Analyse syntaxique de l'instruction de définition de fonction](http://qiita.com/quwahara/items/be71bac4b4359f5e6727#%E6%A7%8B%E6%96] implémentée dans l'article précédent. % 87% E8% A7% A3% E6% 9E% 90parser% E3% 81% AE% E5% AE% 9F% E8% A3% 85% E3% 81% AE% E4% BB% 95% E6% 96% B9 ) Est similaire. Puisqu'il y a un mot-clé «si» au début et que l'ordre d'apparition des jetons qui suivent est également décidé, les jetons sont tracés et analysés dans l'ordre.
Dans l'interpréteur, la méthode body ()
, qui exécute les expressions séquentiellement
Changer pour gérer les instructions if.
C'est parce qu'il est facile à gérer quand il y a un return
dans l'instruction if.
Passez à la mise en œuvre. À propos de l'analyseur syntaxique (Parser) et de l'interpréteur (Interpreter) Jetons un coup d'œil aux modifications et aux ajouts dans l'ordre.
Parser.java
Une implémentation de Parser.java.
Ajoutez une définition de la façon dont cela fonctionne pour la signification du jeton.
Puisque ʻif et ʻelse
sont des mots réservés, je les ai ajoutés là où il y a <-Update
.
Parser.java
public Parser() {
degrees = new HashMap<>();
degrees.put("(", 80);
degrees.put("*", 60);
degrees.put("/", 60);
degrees.put("+", 50);
degrees.put("-", 50);
degrees.put("=", 10);
factorKinds = Arrays.asList(new String[] { "digit", "ident" });
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" }); // <-- Update
}
C'est un changement de pièce à analyser.
Ajout d'un appel à la fonction qui analyse ʻif où se trouve le
<-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")) { // <-- Add
return if_(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;
} else {
throw new Exception("The token cannot place there.");
}
}
C'est un changement de pièce à analyser.
Ajout de la méthode ʻif_ () pour analyser les instructions if. Le résultat de l'analyse de l'instruction if est résumé dans le jeton d'argument. Pour résumer, nous ajoutons des variables de champ à la classe Token. La variable de champ ajoutée est
blockOfElse. Ajouté pour contenir le bloc de traitement du côté ʻelse
de l'instruction if.
Le type est List \ <Token >.
Le traitement dans la méthode ʻif_ () trace les jetons dans la définition de l'instruction if dans l'ordre. Tout d'abord, l'assignation à
token.kind détermine la signification du jeton à ʻif
.
Ensuite, il consomme le (
au début de l'instruction conditionnelle.
La méthode ʻexpression () analyse l'instruction conditionnelle de l'instruction if et la conserve dans
token.left. Ensuite, il consomme la fin
)` de l'instruction conditionnelle.
Analyse des blocs de traitement.
S'il y a un {
au début du bloc, il est entouré d'un jeton {
et d'un jeton }
.
Appelez la méthode body ()
qui analyse le bloc de traitement et maintenez-la dans token.block
.
S'il n'y a pas de {
au début du bloc, on considère qu'il n'y a qu'un seul bloc de traitement et il est stocké dans token.block
.
L'analyse du bloc de traitement de l'autre côté est presque la même.
Parser.java
private Token if_(Token token) throws Exception {
token.kind = "if";
consume("(");
token.left = expression(0);
consume(")");
if (token().value.equals("{")) {
token.block = body();
} else {
token.block = new ArrayList<Token>();
token.block.add(expression(0));
}
if (token().value.equals("else")) {
consume("else");
if (token().value.equals("{")) {
token.blockOfElse = body();
} else {
token.blockOfElse = new ArrayList<Token>();
token.blockOfElse.add(expression(0));
}
}
return token;
}
Analyse le bloc de traitement entouré par le jeton {
et le jeton }
.
Même lorsque le bloc de traitement de la méthode func ()
est analysé
Je faisais la même chose, alors je l'ai changé pour appeler cette méthode.
Parser.java
private List<Token> body() throws Exception {
consume("{");
List<Token> block = block();
consume("}");
return block;
}
Interpreter.java
Une implémentation d'Interpreter.java.
Ceci est une modification de la méthode body ()
qui exécute les expressions séquentiellement.
Ajout d'un appel de méthode pour traiter l'instruction if à // <-Add
.
Ainsi, si return
est appelé dans le bloc de traitement de l'instruction if, il peut revenir au niveau supérieur.
Détermine si ret [0]
est true
et abandonne la méthode body ()
.
Interpreter.java
public Object body(List<Token> body, boolean[] ret) throws Exception {
for (Token exprs : body) {
if (exprs.kind.equals("if")) { // <-- Add
Object val = if_(exprs, ret);
if (ret != null && ret[0]) {
return val;
}
} else if (exprs.kind.equals("ret")) {
if (ret == null) {
throw new Exception("Can not return");
}
ret[0] = true;
if (exprs.left == null) {
return null;
} else {
return expression(exprs.left);
}
} else {
expression(exprs);
}
}
return null;
}
La méthode ʻif_ () qui exécute l'instruction if.
token.leftcontient l'expression conditionnelle. La méthode ʻisTrue ()
détermine si l'expression conditionnelle est vraie.
Si l'expression conditionnelle est vraie, exécutez token.block
.
Si faux, exécutez token.blockOfElse
.
Interpreter.java
public Object if_(Token token, boolean[] ret) throws Exception {
List<Token> block;
if (isTrue(token.left)) {
block = token.block;
} else {
block = token.blockOfElse;
}
if (block != null) {
return body(block, ret);
} else {
return null;
}
}
public boolean isTrue(Token token) throws Exception {
return 0 != value(expression(token));
}
Le programme ci-dessous utilisant l'implémentation ci-dessus
function f(a) {
if (a) {
return 3
} else {
return 4
}
}
println(f(1))
println(f(0))
Pour imprimer «3» et «4» sur la sortie standard en séquence.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "function f(a) {";
text += " if (a) {";
text += " return 3";
text += " } else {";
text += " return 4";
text += " }";
text += "}";
text += "println(f(1))";
text += "println(f(0))";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 3
// --> 4
}
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-10-if-r2/Calc/src/main/java
Il y a un article de suite.
** Correspond à la comparaison et aux opérateurs logiques ** http://qiita.com/quwahara/items/162d01c5af7c69cfa0ed
Recommended Posts