In the previous article, Support multiple arguments. I would like to continue to correspond to the return value of the function.
Make sure you want to do it easily.
For example, if you have a program like the one below, the return value of the ʻadd3 ()function call is returned and assigned to the variable
v. We aim to output the value
6 of the variable
v` to the standard output.
function add3(a1, a2, a3) {
return a1 + a2 + a3
}
v = add3(1,2,3)
println(v)
It's still the previous article that it doesn't correspond to variable scope.
We will consider how to implement it in the order of parser and interpreter. Lexical analysis (Lexer) is not changed.
In implementing the parsing of return
, you need to be careful about the syntax of return
.
The syntax of return
may be a return value, such as return 1
.
In some cases, only return
is specified and no return value is specified.
If only return
is syntactically correct,
When return 1
is written,
It can be taken as the syntax when a return value is specified,
Considering that 1
is a syntax independent of return
,
It can also be used when no return value is specified.
To easily solve this situation, we will take a sloppy approach.
The method is when the }
token comes after the return
token,
It is regarded as return
if no return value is specified.
On the contrary, if the }
token does not come, it is regarded as return
when the return value is specified.
Parsing when no return value is specified is treated in the same way as a variable name.
Parsing when a return value is specified is treated the same as a unary operator such as -1
.
The interpreter makes changes to the method body ()
, which executes expressions sequentially.
If it is a return
token inbody ()
,
Suspends sequential execution and returns to the caller.
Also, return
can only be called inside a function.
It also determines if it is a return
call within the function.
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 return
is a reserved word,
I added return
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" }); // <-- Update
}
It is a change of the part to be analyzed.
Added an if statement that parses return
to where <-Add
exists.
Unless the token next to the return
token is ʻeob, which represents the type of closing brace. Judges as a
return that returns a value, and holds the token that will be the return value in the
left` field.
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")) { // <-- Add
token.kind = "ret";
if (!token().kind.equals("eob")) {
token.left = expression(0);
}
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.");
}
}
Interpreter.java
An implementation of Interpreter.java.
A change to the method body ()
that executes expressions sequentially.
body ()
was a void
type that does not return a return value, but I changed it to a ʻObjecttype so that it can return a return value. Added
boolean [] ret to the signature of
body () .
rethas two roles. The first is that if
ret is not
null, then
returnis possible. The second is that if it was
return, it would propagate to the caller that it was
return. The reason for making
ret an array type of
booleanis to return a value to the caller. It is used in an evil way, not for the purpose of using the original array. I wanted to make it easier to work like an argument with the
ref` keyword in C #.
The first thing in for
is to determine if the token to be executed sequentially is return
.
If it is not return
, it is the same process as the previousbody ()
.
If it is return
, it is judged whether it is possible to return
.
Substitute true
to tell the caller that it was to return
to ret
.
If there is a return value return
, execute the token that is the return value with ʻexpression ()`.
Interpreter.java
public Object body(List<Token> body, boolean[] ret) throws Exception {
for (Token exprs : body) {
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;
}
This is a modification of the ʻinvoke ()method of the
DynamicFuncclass.
<-Updateis the change. It corresponds to the signature of the
body ()method and the change of the return value. You can assign an instance to the argument
ret to indicate that you can
return. The return value of
body () is used as it is as the return value of ʻinvoke ()
.
Interpreter.java
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]; // <-- Update
return context.body(block, ret); // <-- Update
}
}
This is a modification of the run ()
method.
<-Update
is the change.
It corresponds to the signature of the body ()
method and the change of the return value.
The situation where run ()
is called is not inside the function.
Since the situation cannot be return
, the formal argument ret
specifies null
.
Interpreter.java
public Map<String, Variable> run() throws Exception {
body(body, null); // <-- Update
return variables;
}
The program below using the above implementation
function add3(a1, a2, a3) {
return a1 + a2 + a3
}
v = add3(1,2,3)
println(v)
To print the value 6
assigned to the variable v
to the standard output.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "function add3(a1, a2, a3) {";
text += " return a1 + a2 + a3";
text += "}";
text += "v = add3(1,2,3)";
text += "println(v)";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 6
}
That's all for the implementation. Thank you very much.
The full source is available here.
Calc https://github.com/quwahara/Calc/tree/article-9-return-r2/Calc/src/main/java
There is a continuation article.
** Corresponds to if statement ** http://qiita.com/quwahara/items/96a68cdee4f2a0452836
Recommended Posts