In previous articles, Comparison and logical operators and Supports if statements )Did. I would like to support the while statement as an extension of that.
Make sure you want to do it easily.
For example, if you have a program like the one below
Repeat with the while statement, skip the repetition when v
becomes 2
, and aim to output 2
withprintln ()
.
v = 0
while (v < 4) {
v = v + 1
if (v == 2) {
break
}
}
println(v)
How to implement The implementation of the while statement is almost the same as correspondence of if statement. The implementation of the break statement is almost the same as Function return value support. So please refer to that for a detailed explanation and let's implement it immediately.
Move on to implementation. About parser and interpreter Let's take a look at the changes and additions in order.
Parser.java
An implementation of Parser.java.
Add a definition of how it works for the meaning of the token.
Since while
and break
are reserved words, I added them to <-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
}
It is a change of the part to be analyzed.
Added a call to the function that parses while
to the first <-Add
.
Added the parsing of break
to the second <-Add
.
The parsing of break
is done by assigning to token.kind
, determining the meaning of the token to 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.");
}
}
It is a change of the part to be analyzed.
Added a method while_ ()
that parses while statements.
As mentioned above, the analysis of the if statement is a simple explanation.
The analysis result of the while statement is summarized in the argument token.
The processing in the while_ ()
method is designed to trace the tokens in the while statement definition in order.
First, assigning to token.kind
determines the meaning of the token to while
.
Then it consumes the (
at the beginning of the conditional statement.
The ʻexpression () method parses the conditional statement of the while statement and holds it in
token.left. Then it consumes the end
)` of the conditional statement.
Analysis of processing blocks.
If there is a {
at the beginning of the block, it is surrounded by {
tokens and}
tokens.
Call the body ()
method that parses the processing block and hold it in token.block
.
If there is no {
at the beginning of the block, it is considered that there is only one processing block and it is stored in 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
An implementation of Interpreter.java.
A change to the method body ()
that executes expressions sequentially.
Added boolean [] brk
to the signature ofbody ()
.
brk
has two roles.
The first is that if brk
is not null
, then break
is possible.
The second is that if it was break
, it will be propagated to the caller as well.
The reason for making brk
an array type of boolean
is to return a value to the caller.
At // <-Add 2
, it is determined whether the token to be executed sequentially is break
.
If the token is break
, determine if it is possible to break
.
Substitute true
to tell the caller that it was break
to brk
.
Added a method call to process while statements to // <-Add 1
.
So that when return
is called in the processing block of the while statement, it can return to the upper level.
Determines if ret [0]
is true
and aborts thebody ()
method.
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;
}
A while_ ()
method that executes a while statement.
token.left
holds the conditional expression.
The ʻisTrue ()method determines if the conditional expression is true. Repeat
token.blockwhile the conditional expression is true. However, even if the conditional expression is true, in the
body ()method call, If it is a
return or
break` token, the iteration will be interrupted.
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;
}
Next is the response to changing the signature of the body ()
method.
Break
may also be called in the if statement.
Therefore, add a brk
formal argument to the signature of the ʻif_ ()method, Pass it in the call to the
body ()` method.
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;
}
}
This is a response to the change in the signature of the body ()
method.
In the call to the body ()
method in <-Update
Because it is a process in a context where the break
call cannot be made
The argument brk
of thebody ()
method is called with 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
}
}
The program below using the above implementation
v = 0
while (v < 4) {
v = v + 1
if (v == 2) {
break
}
}
println(v)
To print 2
to standard output in sequence.
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
}
That's all for the implementation. Thank you very much.
The full source is available here.
Calc https://github.com/quwahara/Calc/tree/article-12-while-r2/Calc/src/main/java
There is a continuation article.
** Corresponds to Scope ** http://qiita.com/quwahara/items/d9f932195da1b655b617
Recommended Posts