Dans les articles précédents, Comparaison et opérateurs logiques et Prend en charge les instructions if )Fait. Je voudrais soutenir la déclaration while comme une extension de cela.
Assurez-vous de vouloir le faire facilement. Par exemple, si vous avez un programme comme celui ci-dessous Répétez avec l'instruction while, ignorez la répétition lorsque «v» devient «2» et visez à afficher «2» avec «println ()».
v = 0
while (v < 4) {
v = v + 1
if (v == 2) {
break
}
}
println(v)
Comment mettre en œuvre La mise en œuvre de l'instruction while est presque la même que correspondance de l'instruction if. L'implémentation de l'instruction break est presque identique à Prise en charge de la valeur de retour de fonction. Veuillez donc vous y référer pour une explication détaillée et implémentons-le immédiatement.
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 «while» et «break» sont des mots réservés, je les ai ajoutés à «<-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("==", 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);
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", "while", "break"}); // <-- Update
}
C'est un changement de la pièce à analyser.
Ajout d'un appel à la fonction qui analyse while
au premier <-Add
.
Ajout de l'analyse break
au second <-Add
.
L'analyse de "break" se fait en attribuant à "token.kind", déterminant la signification du token à "brk".
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")) {
return if_(token);
} else if (token.kind.equals("ident") && token.value.equals("while")) { // <-- Add
return while_(token);
} else if (token.kind.equals("ident") && token.value.equals("break")) { // <-- Add
token.kind = "brk";
return 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 la pièce à analyser.
Ajout d'une méthode while_ ()
qui analyse les instructions while.
Comme mentionné ci-dessus, l'analyse de l'instruction if est une explication simple.
Le résultat de l'analyse de l'instruction while est résumé dans le jeton d'argument.
Le traitement dans la méthode while_ ()
est conçu pour tracer les jetons dans la définition de l'instruction while dans l'ordre.
Tout d'abord, l'attribution à token.kind
détermine la signification du jeton à while
.
Ensuite, il consomme le (
au début de l'instruction conditionnelle.
La méthode ʻexpression () analyse l'instruction conditionnelle de l'instruction while 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
.
Parser.java
private Token while_(Token token) throws Exception {
token.kind = "while";
consume("(");
token.left = expression(0);
consume(")");
if (token().value.equals("{")) {
token.block = body();
} else {
token.block = new ArrayList<Token>();
token.block.add(expression(0));
}
return token;
}
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 de boolean [] brk
à la signature de body ()
.
brk
a deux rôles.
La première est que si «brk» n'est pas «null», alors «break» est possible.
La seconde est que s'il s'agissait de "break", il sera également propagé à l'appelant.
La raison pour laquelle «brk» est un tableau de type «booléen» est de renvoyer une valeur à l'appelant.
À «// <-Add 2», il est déterminé si le jeton à exécuter séquentiellement est «break». Si le jeton est «break», déterminez s'il est possible de «casser». Remplacez «true» pour dire à l'appelant que c'était «break» par «brk».
Ajout d'un appel de méthode pour traiter les instructions while à // <-Add 1
.
Ainsi, lorsque return
est appelé dans le bloc de traitement de l'instruction while, 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, boolean[] brk) throws Exception {
for (Token exprs : body) {
if (exprs.kind.equals("if")) {
Object val = if_(exprs, ret, brk);
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 if (exprs.kind.equals("while")) { // <-- Add 1
Object val = while_(exprs, ret);
if (ret != null && ret[0]) {
return val;
}
} else if (exprs.kind.equals("brk")) { // <-- Add 2
if (brk == null) {
throw new Exception("Can not break");
}
brk[0] = true;
return null;
} else {
expression(exprs);
}
}
return null;
}
Une méthode while_ ()
qui exécute une instruction while.
token.left
contient l'expression conditionnelle.
La méthode ʻisTrue () détermine si l'expression conditionnelle est vraie. Répétez
token.blocktant que l'expression conditionnelle est vraie. Cependant, même si l'expression conditionnelle est vraie, dans l'appel de la méthode
body ()`
S'il s'agit d'un jeton «return» ou «break», l'itération sera interrompue.
Interpreter.java
public Object while_(Token token, boolean[] ret) throws Exception {
boolean[] brk = new boolean[1];
Object val;
while (isTrue(token.left)) {
val = body(token.block, ret, brk);
if (ret != null && ret[0]) {
return val;
}
if (brk[0]) {
return null;
}
}
return null;
}
Vient ensuite la réponse au changement de signature de la méthode body ()
.
Break
peut également être appelé dans l'instruction if.
Par conséquent, ajoutez un argument formel brk
à la signature de la méthode ʻif_ (), Passez-le dans l'appel à la méthode
body ()`.
Interpreter.java
public Object if_(Token token, boolean[] ret, boolean[] brk) throws Exception {
List<Token> block;
if (isTrue(token.left)) {
block = token.block;
} else {
block = token.blockOfElse;
}
if (block != null) {
return body(block, ret, brk);
} else {
return null;
}
}
Ceci est une réponse au changement de signature de la méthode body ()
.
Dans l'appel à la méthode body ()
dans <-Update
Parce que c'est un processus dans un contexte où l'appel break
ne peut pas être effectué
L'argument brk
de la méthode body ()
est appelé avec null
.
Interpreter.java
public Map<String, Variable> run() throws Exception {
body(body, null, null); // <-- Update
return variables;
}
public static class DynamicFunc extends Func {
public Interpreter context;
public List<Token> params;
public List<Token> block;
@Override
public Object invoke(List<Object> args) throws Exception {
for (int i = 0; i < params.size(); ++i) {
Token param = params.get(i);
Variable v = context.variable(context.ident(param));
if (i < args.size()) {
v.value = context.value(args.get(i));
} else {
v.value = null;
}
}
boolean[] ret = new boolean[1];
return context.body(block, ret, null); // <-- Update
}
}
Le programme ci-dessous utilisant l'implémentation ci-dessus
v = 0
while (v < 4) {
v = v + 1
if (v == 2) {
break
}
}
println(v)
Pour imprimer «2» sur la sortie standard en séquence.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "v = 0";
text += "while (v < 4) {";
text += " v = v + 1";
text += " if (v == 2) {";
text += " break";
text += " }";
text += "}";
text += "println(v)";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 2
}
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-12-while-r2/Calc/src/main/java
Il y a un article de suite.
** Correspond à la portée ** http://qiita.com/quwahara/items/d9f932195da1b655b617
Recommended Posts