mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Урок 6. Улучшаем логические выражения и лексер
This commit is contained in:
parent
0feac2a331
commit
b7522f6459
@ -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"
|
@ -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]);
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -21,8 +21,18 @@ public enum TokenType {
|
||||
STAR,
|
||||
SLASH,
|
||||
EQ,
|
||||
EQEQ,
|
||||
EXCL,
|
||||
EXCLEQ,
|
||||
LT,
|
||||
LTEQ,
|
||||
GT,
|
||||
GTEQ,
|
||||
|
||||
BAR,
|
||||
BARBAR,
|
||||
AMP,
|
||||
AMPAMP,
|
||||
|
||||
LPAREN, // (
|
||||
RPAREN, // )
|
||||
|
@ -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("/"),
|
||||
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user