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 "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"
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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, // )
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user