diff --git a/program.txt b/program.txt index bf2b398..029fe18 100644 --- a/program.txt +++ b/program.txt @@ -3,4 +3,9 @@ word2 = PI + word str = "a" * 5 + "ba" * 7 + "\n" print str * "3" * 2 print "word = " + word + "\n" -print "word2 = " + word2 +print "word2 = " + word2 + "\n" +print "1" > "abc" +print "\n" +if (1 < 2) print "1 = 1" +else print "1 != 1" +print "\n" diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/com/annimon/ownlang/lib/NumberValue.java index 62cc525..f9e5256 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -7,6 +7,10 @@ package com.annimon.ownlang.lib; public final class NumberValue implements Value { private final double value; + + public NumberValue(boolean value) { + this.value = value ? 1 : 0; + } public NumberValue(double value) { this.value = value; diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index da21488..4277bf4 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -9,12 +9,12 @@ import java.util.List; */ 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, TokenType.STAR, TokenType.SLASH, TokenType.LPAREN, TokenType.RPAREN, - TokenType.EQ + TokenType.EQ, TokenType.LT, TokenType.GT }; private final String input; @@ -98,10 +98,13 @@ public final class Lexer { } final String word = buffer.toString(); - if (word.equals("print")) { - addToken(TokenType.PRINT); - } else { - addToken(TokenType.WORD, word); + switch (word) { + case "print": addToken(TokenType.PRINT); break; + case "if": addToken(TokenType.IF); break; + case "else": addToken(TokenType.ELSE); break; + default: + addToken(TokenType.WORD, word); + break; } } diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index f8406b3..4e9627a 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -3,8 +3,10 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.parser.ast.PrintStatement; import com.annimon.ownlang.parser.ast.AssignmentStatement; import com.annimon.ownlang.parser.ast.BinaryExpression; +import com.annimon.ownlang.parser.ast.ConditionalExpression; import com.annimon.ownlang.parser.ast.VariabletExpression; import com.annimon.ownlang.parser.ast.Expression; +import com.annimon.ownlang.parser.ast.IfStatement; import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.UnaryExpression; @@ -41,6 +43,9 @@ public final class Parser { if (match(TokenType.PRINT)) { return new PrintStatement(expression()); } + if (match(TokenType.IF)) { + return ifElse(); + } return assignmentStatement(); } @@ -55,10 +60,43 @@ public final class Parser { throw new RuntimeException("Unknown statement"); } + private Statement ifElse() { + final Expression condition = expression(); + final Statement ifStatement = statement(); + final Statement elseStatement; + if (match(TokenType.ELSE)) { + elseStatement = statement(); + } else { + elseStatement = null; + } + return new IfStatement(condition, ifStatement, elseStatement); + } private Expression expression() { - return additive(); + return conditional(); + } + + private Expression conditional() { + Expression result = additive(); + + while (true) { + if (match(TokenType.EQ)) { + result = new ConditionalExpression('=', result, additive()); + continue; + } + if (match(TokenType.LT)) { + result = new ConditionalExpression('<', result, additive()); + continue; + } + if (match(TokenType.GT)) { + result = new ConditionalExpression('>', result, additive()); + continue; + } + break; + } + + return result; } private Expression additive() { diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 80a2815..03ad2a7 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -13,12 +13,16 @@ public enum TokenType { // keyword PRINT, + IF, + ELSE, PLUS, MINUS, STAR, SLASH, EQ, + LT, + GT, LPAREN, // ( RPAREN, // ) diff --git a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java new file mode 100644 index 0000000..1ded250 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -0,0 +1,53 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class ConditionalExpression implements Expression { + + private final Expression expr1, expr2; + private final char operation; + + public ConditionalExpression(char operation, Expression expr1, Expression expr2) { + this.operation = operation; + this.expr1 = expr1; + this.expr2 = expr2; + } + + @Override + public Value eval() { + final Value value1 = expr1.eval(); + final Value value2 = expr2.eval(); + 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)); + } + } + + final double number1 = value1.asNumber(); + final double number2 = value2.asNumber(); + switch (operation) { + case '<': return new NumberValue(number1 < number2); + case '>': return new NumberValue(number1 > number2); + case '=': + default: + return new NumberValue(number1 == number2); + } + } + + @Override + public String toString() { + return String.format("[%s %c %s]", expr1, operation, expr2); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/IfStatement.java b/src/com/annimon/ownlang/parser/ast/IfStatement.java new file mode 100644 index 0000000..87be67d --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/IfStatement.java @@ -0,0 +1,37 @@ +package com.annimon.ownlang.parser.ast; + +/** + * + * @author aNNiMON + */ +public final class IfStatement implements Statement { + + private final Expression expression; + private final Statement ifStatement, elseStatement; + + public IfStatement(Expression expression, Statement ifStatement, Statement elseStatement) { + this.expression = expression; + this.ifStatement = ifStatement; + this.elseStatement = elseStatement; + } + + @Override + public void execute() { + final double result = expression.eval().asNumber(); + if (result != 0) { + ifStatement.execute(); + } else if (elseStatement != null) { + elseStatement.execute(); + } + } + + @Override + public String toString() { + final StringBuilder result = new StringBuilder(); + result.append("if ").append(expression).append(' ').append(ifStatement); + if (elseStatement != null) { + result.append("\nelse ").append(elseStatement); + } + return result.toString(); + } +}