Corresponding to the return value of the function in the previous article. Next, I would like to support if statements.
Make sure you want to do it easily.
For example, if you have a program like the one below, the first println ()
will output 3
and
The next println ()
aims to print 4
.
For the sake of simplicity, the authenticity of the if statement is determined by assuming that non-zero is true and 0 is false.
function f(a) {
if (a) {
return 3
} else {
return 4
}
}
println(f(1))
println(f(0))
We will consider how to implement it in the order of parser and interpreter. Lexical analysis (Lexer) is not changed.
For how to implement if statement parsing, see [Function definition parsing](http://qiita.com/quwahara/items/be71bac4b4359f5e6727#%E6%A7%8B%E6%96] implemented in the previous article. % 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 ) Is similar. Since there is a keyword ʻif` at the beginning and the order of appearance of the tokens that follow it is also decided, the tokens are traced and analyzed in order.
In the interpreter, the method body ()
, which executes expressions sequentially,
Change to handle if statements.
This is because it is easy to handle when there is a return
in the if statement.
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 ʻif and ʻelse
are reserved words, I added them where there is <-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
}
It is a change of the part to be analyzed.
Added a function call to parse ifwhere
<-Add` is.
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.");
}
}
It is a change of the part to be analyzed.
Added the method ʻif_ ()to parse if statements. The analysis result of the if statement is summarized in the argument token. To summarize, we are adding field variables to the Token class. The added field variable is
blockOfElse. Added to hold the processing block on the ʻelse
side of the if statement.
The type is List \ <Token >.
The processing in the ʻif_ ()method is designed to trace the tokens in the if statement definition in order. First, assigning to
token.kind determines the meaning of the token to ʻif
.
Then it consumes the (
at the beginning of the conditional statement.
The ʻexpression () method parses the conditional statement of the if 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
.
The analysis of the processing block on the else side is almost the same.
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;
}
Parses the processing block enclosed by the {
token and the }
token.
Even in the part that parses the processing block of the func ()
method
I was doing the same thing, so I changed it to call this method.
Parser.java
private List<Token> body() throws Exception {
consume("{");
List<Token> block = block();
consume("}");
return block;
}
Interpreter.java
An implementation of Interpreter.java.
A change to the method body ()
that executes expressions sequentially.
Added a method call to process if statement to // <-Add
.
So that if return
is called in the processing block of the if 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) 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;
}
A ʻif_ ()method that executes an if statement.
token.left holds the conditional expression. The ʻisTrue ()
method determines if the conditional expression is true.
If the conditional expression is true, execute token.block
.
If false, run 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));
}
The program below using the above implementation
function f(a) {
if (a) {
return 3
} else {
return 4
}
}
println(f(1))
println(f(0))
To print 3
and 4
to standard output in sequence.
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
}
That's all for the implementation. Thank you very much.
The full source is available here.
Calc https://github.com/quwahara/Calc/tree/article-10-if-r2/Calc/src/main/java
There is a continuation article.
** Corresponds to comparison and logical operators ** http://qiita.com/quwahara/items/162d01c5af7c69cfa0ed
Recommended Posts