Урок 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 "word2 = " + word2 + "\n"
print "1" > "abc" print "1" > "abc"
print "\n" print "\n"
if (1 < 2) print "1 = 1" if (1 <= 2) print "1 = 1"
else print "1 != 1" else print "1 != 1"
print "\n" print "\n"
if (40 < 50 && 50 > 60) print "true"
else print "false"

View File

@ -1,7 +1,9 @@
package com.annimon.ownlang.parser; package com.annimon.ownlang.parser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* *
@ -9,13 +11,33 @@ import java.util.List;
*/ */
public final class Lexer { public final class Lexer {
private static final String OPERATOR_CHARS = "+-*/()=<>"; private static final String OPERATOR_CHARS = "+-*/()=<>!&|";
private static final TokenType[] OPERATOR_TOKENS = {
TokenType.PLUS, TokenType.MINUS, private static final Map<String, TokenType> OPERATORS;
TokenType.STAR, TokenType.SLASH, static {
TokenType.LPAREN, TokenType.RPAREN, OPERATORS = new HashMap<>();
TokenType.EQ, TokenType.LT, TokenType.GT 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 String input;
private final int length; private final int length;
@ -81,9 +103,30 @@ public final class Lexer {
} }
private void tokenizeOperator() { private void tokenizeOperator() {
final int position = OPERATOR_CHARS.indexOf(peek(0)); char current = peek(0);
addToken(OPERATOR_TOKENS[position]); if (current == '/') {
if (peek(1) == '/') {
next(); 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() { private void tokenizeWord() {
@ -132,6 +175,24 @@ public final class Lexer {
addToken(TokenType.TEXT, buffer.toString()); 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() { private char next() {
pos++; pos++;
return peek(0); return peek(0);

View File

@ -74,23 +74,68 @@ public final class Parser {
private Expression expression() { 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() { private Expression conditional() {
Expression result = additive(); Expression result = additive();
while (true) { while (true) {
if (match(TokenType.EQ)) { if (match(TokenType.LT)) {
result = new ConditionalExpression('=', result, additive()); result = new ConditionalExpression(ConditionalExpression.Operator.LT, result, additive());
continue; continue;
} }
if (match(TokenType.LT)) { if (match(TokenType.LTEQ)) {
result = new ConditionalExpression('<', result, additive()); result = new ConditionalExpression(ConditionalExpression.Operator.LTEQ, result, additive());
continue; continue;
} }
if (match(TokenType.GT)) { 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; continue;
} }
break; break;

View File

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

View File

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