Урок 6. Улучшаем логические выражения и лексер

This commit is contained in:
Victor 2015-06-04 18:44:32 +03:00
parent 0feac2a331
commit b7522f6459
5 changed files with 187 additions and 36 deletions

View File

@ -6,6 +6,8 @@ print "word = " + word + "\n"
print "word2 = " + word2 + "\n"
print "1" > "abc"
print "\n"
if (1 < 2) print "1 = 1"
if (1 <= 2) print "1 = 1"
else print "1 != 1"
print "\n"
if (40 < 50 && 50 > 60) print "true"
else print "false"

View File

@ -1,7 +1,9 @@
package com.annimon.ownlang.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
@ -9,13 +11,33 @@ import java.util.List;
*/
public final class Lexer {
private static final String OPERATOR_CHARS = "+-*/()=<>";
private static final TokenType[] OPERATOR_TOKENS = {
TokenType.PLUS, TokenType.MINUS,
TokenType.STAR, TokenType.SLASH,
TokenType.LPAREN, TokenType.RPAREN,
TokenType.EQ, TokenType.LT, TokenType.GT
};
private static final String OPERATOR_CHARS = "+-*/()=<>!&|";
private static final Map<String, TokenType> OPERATORS;
static {
OPERATORS = new HashMap<>();
OPERATORS.put("+", TokenType.PLUS);
OPERATORS.put("-", TokenType.MINUS);
OPERATORS.put("*", TokenType.STAR);
OPERATORS.put("/", TokenType.SLASH);
OPERATORS.put("(", TokenType.LPAREN);
OPERATORS.put(")", TokenType.RPAREN);
OPERATORS.put("=", TokenType.EQ);
OPERATORS.put("<", TokenType.LT);
OPERATORS.put(">", TokenType.GT);
OPERATORS.put("!", TokenType.EXCL);
OPERATORS.put("&", TokenType.AMP);
OPERATORS.put("|", TokenType.BAR);
OPERATORS.put("==", TokenType.EQEQ);
OPERATORS.put("!=", TokenType.EXCLEQ);
OPERATORS.put("<=", TokenType.LTEQ);
OPERATORS.put(">=", TokenType.GTEQ);
OPERATORS.put("&&", TokenType.AMPAMP);
OPERATORS.put("||", TokenType.BARBAR);
}
private final String input;
private final int length;
@ -81,9 +103,30 @@ public final class Lexer {
}
private void tokenizeOperator() {
final int position = OPERATOR_CHARS.indexOf(peek(0));
addToken(OPERATOR_TOKENS[position]);
next();
char current = peek(0);
if (current == '/') {
if (peek(1) == '/') {
next();
next();
tokenizeComment();
return;
} else if (peek(1) == '*') {
next();
next();
tokenizeMultilineComment();
return;
}
}
final StringBuilder buffer = new StringBuilder();
while (true) {
final String text = buffer.toString();
if (!OPERATORS.containsKey(text + current) && !text.isEmpty()) {
addToken(OPERATORS.get(text));
return;
}
buffer.append(current);
current = next();
}
}
private void tokenizeWord() {
@ -132,6 +175,24 @@ public final class Lexer {
addToken(TokenType.TEXT, buffer.toString());
}
private void tokenizeComment() {
char current = peek(0);
while ("\r\n\0".indexOf(current) == -1) {
current = next();
}
}
private void tokenizeMultilineComment() {
char current = peek(0);
while (true) {
if (current == '\0') throw new RuntimeException("Missing close tag");
if (current == '*' && peek(1) == '/') break;
current = next();
}
next(); // *
next(); // /
}
private char next() {
pos++;
return peek(0);

View File

@ -74,23 +74,68 @@ public final class Parser {
private Expression expression() {
return conditional();
return logicalOr();
}
private Expression logicalOr() {
Expression result = logicalAnd();
while (true) {
if (match(TokenType.BARBAR)) {
result = new ConditionalExpression(ConditionalExpression.Operator.OR, result, logicalAnd());
continue;
}
break;
}
return result;
}
private Expression logicalAnd() {
Expression result = equality();
while (true) {
if (match(TokenType.AMPAMP)) {
result = new ConditionalExpression(ConditionalExpression.Operator.AND, result, equality());
continue;
}
break;
}
return result;
}
private Expression equality() {
Expression result = conditional();
if (match(TokenType.EQEQ)) {
return new ConditionalExpression(ConditionalExpression.Operator.EQUALS, result, conditional());
}
if (match(TokenType.EXCLEQ)) {
return new ConditionalExpression(ConditionalExpression.Operator.NOT_EQUALS, result, conditional());
}
return result;
}
private Expression conditional() {
Expression result = additive();
while (true) {
if (match(TokenType.EQ)) {
result = new ConditionalExpression('=', result, additive());
if (match(TokenType.LT)) {
result = new ConditionalExpression(ConditionalExpression.Operator.LT, result, additive());
continue;
}
if (match(TokenType.LT)) {
result = new ConditionalExpression('<', result, additive());
if (match(TokenType.LTEQ)) {
result = new ConditionalExpression(ConditionalExpression.Operator.LTEQ, result, additive());
continue;
}
if (match(TokenType.GT)) {
result = new ConditionalExpression('>', result, additive());
result = new ConditionalExpression(ConditionalExpression.Operator.GT, result, additive());
continue;
}
if (match(TokenType.GTEQ)) {
result = new ConditionalExpression(ConditionalExpression.Operator.GTEQ, result, additive());
continue;
}
break;

View File

@ -21,8 +21,18 @@ public enum TokenType {
STAR,
SLASH,
EQ,
EQEQ,
EXCL,
EXCLEQ,
LT,
LTEQ,
GT,
GTEQ,
BAR,
BARBAR,
AMP,
AMPAMP,
LPAREN, // (
RPAREN, // )

View File

@ -10,10 +10,38 @@ import com.annimon.ownlang.lib.Value;
*/
public final class ConditionalExpression implements Expression {
private final Expression expr1, expr2;
private final char operation;
public static enum Operator {
PLUS("+"),
MINUS("-"),
MULTIPLY("*"),
DIVIDE("/"),
EQUALS("=="),
NOT_EQUALS("!="),
LT("<"),
LTEQ("<="),
GT(">"),
GTEQ(">="),
AND("&&"),
OR("||");
private final String name;
public ConditionalExpression(char operation, Expression expr1, Expression expr2) {
private Operator(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
private final Expression expr1, expr2;
private final Operator operation;
public ConditionalExpression(Operator operation, Expression expr1, Expression expr2) {
this.operation = operation;
this.expr1 = expr1;
this.expr2 = expr2;
@ -23,31 +51,36 @@ public final class ConditionalExpression implements Expression {
public Value eval() {
final Value value1 = expr1.eval();
final Value value2 = expr2.eval();
double number1, number2;
if (value1 instanceof StringValue) {
final String string1 = value1.asString();
final String string2 = value2.asString();
switch (operation) {
case '<': return new NumberValue(string1.compareTo(string2) < 0);
case '>': return new NumberValue(string1.compareTo(string2) > 0);
case '=':
default:
return new NumberValue(string1.equals(string2));
}
number1 = value1.asString().compareTo(value2.asString());
number2 = 0;
} else {
number1 = value1.asNumber();
number2 = value2.asNumber();
}
final double number1 = value1.asNumber();
final double number2 = value2.asNumber();
boolean result;
switch (operation) {
case '<': return new NumberValue(number1 < number2);
case '>': return new NumberValue(number1 > number2);
case '=':
case LT: result = number1 < number2; break;
case LTEQ: result = number1 <= number2; break;
case GT: result = number1 > number2; break;
case GTEQ: result = number1 >= number2; break;
case NOT_EQUALS: result = number1 != number2; break;
case AND: result = (number1 != 0) && (number2 != 0); break;
case OR: result = (number1 != 0) || (number2 != 0); break;
case EQUALS:
default:
return new NumberValue(number1 == number2);
result = number1 == number2; break;
}
return new NumberValue(result);
}
@Override
public String toString() {
return String.format("[%s %c %s]", expr1, operation, expr2);
return String.format("[%s %s %s]", expr1, operation.getName(), expr2);
}
}