In the previous article, Supports if statements. Since there is no operator that can be specified in the conditional statement of the if statement, I would like to support it.
Make sure you want to do it easily.
For example, there is the following program.
Since the inequality sign is correct on the first line, 1
representing the truth is output.
Since the inequality sign with the equal sign is wrong on the second line, 0
indicating false is output.
The third line outputs 1
because the result of the logical operation is true.
In the example, it is only output, but it can be described as a conditional expression in the if statement.
println(10 > 1)
println(5 <= 1)
println(!(1 == 2) && (3 != 4))
For the implementation method, see Implementation of parsing and Implementation of interpreter. Because of that Please refer to that because there is nothing new.
Move on to implementation. About lexical analysis (Lexer), parser (Parser), interpreter (Interpreter) Let's take a look at the changes and additions in order.
Lexer.java
An implementation of Lexer.java. Corresponds to the added operator. Until now, all operators were one-letter, but it may be new that two-letter operators have also increased.
Lexer.java
private boolean isSignStart(char c) {
return c == '=' || c == '+' || c == '-' || c == '*' || c == '/' || c == '!' || c == '<' || c == '>' || c == '&'
|| c == '|';
}
private Token sign() throws Exception {
Token t = new Token();
t.kind = "sign";
char c1 = next();
char c2 = (char) 0;
if (!isEOT()) {
if (c1 == '=' || c1 == '!' || c1 == '<' || c1 == '>') {
if (c() == '=') {
c2 = next();
}
} else if (c1 == '&') {
if (c() == '&') {
c2 = next();
}
} else if (c1 == '|') {
if (c() == '|') {
c2 = next();
}
}
}
String v;
if (c2 == (char) 0) {
v = Character.toString(c1);
} else {
v = Character.toString(c1) + Character.toString(c2);
}
t.value = v;
return t;
}
Parser.java
An implementation of Parser.java.
Add a definition of how it works for the meaning of the token.
A new operator has been added from <-Add
.
Since !
Is a unary operator, it is added 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); // <-- Add
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[] { "+", "-", "!" }); // <-- Update
reserved = Arrays.asList(new String[] { "function", "return", "if", "else" });
}
Interpreter.java
An implementation of Interpreter.java.
Since it is convenient for the method to judge whether the conditional expression is true, we added diversity by different argument types.
Interpreter.java
public boolean isTrue(Token token) throws Exception {
return isTrue(value(expression(token)));
}
public boolean isTrue(Integer value) throws Exception {
return 0 != value;
}
Truth values are represented by integers in this language, so prepare a method to convert the logical value type to an integer.
True is converted to 1
.
Interpreter.java
public Integer toInteger(boolean b) {
return b ? 1 : 0;
}
A method that executes a unary operator.
Added logical negation to <-Add
.
Interpreter.java
public Object unaryCalc(Token expr) throws Exception {
Integer left = value(expression(expr.left));
if (expr.value.equals("+")) {
return left;
} else if (expr.value.equals("-")) {
return -left;
} else if (expr.value.equals("!")) { // <-- Add
return toInteger(!isTrue(left));
} else {
throw new Exception("Unknown sign for unary calc");
}
}
A method that executes a binary operator.
From <-Add
, we are adding a new arithmetic implementation.
Interpreter.java
public Object calc(Token expr) throws Exception {
Integer left = value(expression(expr.left));
Integer right = value(expression(expr.right));
if (expr.value.equals("+")) {
return left + right;
} else if (expr.value.equals("-")) {
return left - right;
} else if (expr.value.equals("*")) {
return left * right;
} else if (expr.value.equals("/")) {
return left / right;
} else if (expr.value.equals("==")) { // <-- Add
return toInteger(left == right);
} else if (expr.value.equals("!=")) {
return toInteger(left != right);
} else if (expr.value.equals("<")) {
return toInteger(left < right);
} else if (expr.value.equals("<=")) {
return toInteger(left <= right);
} else if (expr.value.equals(">")) {
return toInteger(left > right);
} else if (expr.value.equals(">=")) {
return toInteger(left >= right);
} else if (expr.value.equals("&&")) {
return toInteger(isTrue(left) && isTrue(right));
} else if (expr.value.equals("||")) {
return toInteger(isTrue(left) || isTrue(right));
} else {
throw new Exception("Unknown sign for Calc");
}
}
The program below using the above implementation
println(10 > 1)
println(5 <= 1)
println(!(1 == 2) && (3 != 4))
Is executed, and 1
, 0
, and 1
are output to the standard output.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "println(10 > 1)";
text += "println(5 <= 1)";
text += "println(!(1 == 2) && (3 != 4))";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 1
// --> 0
// --> 1
}
That's all for the implementation. Thank you very much.
The full source is available here.
Calc https://github.com/quwahara/Calc/tree/article-11-compare-r2/Calc/src/main/java
There is a continuation article.
** Corresponds to the while statement ** http://qiita.com/quwahara/items/36f6704ae9c756240068
Recommended Posts