From c842dd0de36b269fd034901958716e1aa82d0ef9 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 25 May 2015 23:20:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D1=80=D0=BE=D0=BA=204.=20=D0=A1=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D0=B8,=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=20print?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.txt | 6 ++++ src/com/annimon/ownlang/Main.java | 13 ++++--- .../NumberValue.java} | 19 ++++++---- src/com/annimon/ownlang/lib/StringValue.java | 34 ++++++++++++++++++ src/com/annimon/ownlang/lib/Value.java | 12 +++++++ src/com/annimon/ownlang/lib/Variables.java | 17 ++++----- src/com/annimon/ownlang/parser/Lexer.java | 33 ++++++++++++++++- src/com/annimon/ownlang/parser/Parser.java | 13 +++++-- src/com/annimon/ownlang/parser/TokenType.java | 4 +++ .../parser/ast/AssignmentStatement.java | 3 +- .../ownlang/parser/ast/BinaryExpression.java | 35 ++++++++++++++++--- .../ownlang/parser/ast/Expression.java | 4 ++- .../ownlang/parser/ast/PrintStatement.java | 24 +++++++++++++ .../ownlang/parser/ast/UnaryExpression.java | 7 ++-- .../ownlang/parser/ast/ValueExpression.java | 32 +++++++++++++++++ .../parser/ast/VariabletExpression.java | 3 +- 16 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 program.txt rename src/com/annimon/ownlang/{parser/ast/NumberExpression.java => lib/NumberValue.java} (51%) create mode 100644 src/com/annimon/ownlang/lib/StringValue.java create mode 100644 src/com/annimon/ownlang/lib/Value.java create mode 100644 src/com/annimon/ownlang/parser/ast/PrintStatement.java create mode 100644 src/com/annimon/ownlang/parser/ast/ValueExpression.java diff --git a/program.txt b/program.txt new file mode 100644 index 0000000..bf2b398 --- /dev/null +++ b/program.txt @@ -0,0 +1,6 @@ +word = 2 + 2 +word2 = PI + word +str = "a" * 5 + "ba" * 7 + "\n" +print str * "3" * 2 +print "word = " + word + "\n" +print "word2 = " + word2 diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index d8ddbab..b5758c2 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -5,6 +5,9 @@ import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; /** @@ -12,11 +15,9 @@ import java.util.List; */ public final class Main { - public static void main(String[] args) { - final String input1 = "word = 2 + 2\nword2 = PI + word"; -// final String input2 = "(GOLDEN_RATIO + 2) * #f"; - final String input2 = "GOLDEN_RATIO"; - final List tokens = new Lexer(input1).tokenize(); + public static void main(String[] args) throws IOException { + final String input = new String( Files.readAllBytes(Paths.get("program.txt")), "UTF-8"); + final List tokens = new Lexer(input).tokenize(); for (Token token : tokens) { System.out.println(token); } @@ -28,7 +29,5 @@ public final class Main { for (Statement statement : statements) { statement.execute(); } - System.out.printf("%s = %f\n", "word", Variables.get("word")); - System.out.printf("%s = %f\n", "word2", Variables.get("word2")); } } diff --git a/src/com/annimon/ownlang/parser/ast/NumberExpression.java b/src/com/annimon/ownlang/lib/NumberValue.java similarity index 51% rename from src/com/annimon/ownlang/parser/ast/NumberExpression.java rename to src/com/annimon/ownlang/lib/NumberValue.java index 7e06dc5..62cc525 100644 --- a/src/com/annimon/ownlang/parser/ast/NumberExpression.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -1,24 +1,29 @@ -package com.annimon.ownlang.parser.ast; +package com.annimon.ownlang.lib; /** * * @author aNNiMON */ -public final class NumberExpression implements Expression { +public final class NumberValue implements Value { private final double value; - - public NumberExpression(double value) { + + public NumberValue(double value) { this.value = value; } - + @Override - public double eval() { + public double asNumber() { return value; } @Override - public String toString() { + public String asString() { return Double.toString(value); } + + @Override + public String toString() { + return asString(); + } } diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java new file mode 100644 index 0000000..d25f763 --- /dev/null +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -0,0 +1,34 @@ +package com.annimon.ownlang.lib; + +/** + * + * @author aNNiMON + */ +public final class StringValue implements Value { + + private final String value; + + public StringValue(String value) { + this.value = value; + } + + @Override + public double asNumber() { + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return 0; + } + } + + @Override + public String asString() { + return value; + } + + @Override + public String toString() { + return asString(); + } + +} diff --git a/src/com/annimon/ownlang/lib/Value.java b/src/com/annimon/ownlang/lib/Value.java new file mode 100644 index 0000000..9e8ea09 --- /dev/null +++ b/src/com/annimon/ownlang/lib/Value.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.lib; + +/** + * + * @author aNNiMON + */ +public interface Value { + + double asNumber(); + + String asString(); +} diff --git a/src/com/annimon/ownlang/lib/Variables.java b/src/com/annimon/ownlang/lib/Variables.java index 4913c51..43f1d72 100644 --- a/src/com/annimon/ownlang/lib/Variables.java +++ b/src/com/annimon/ownlang/lib/Variables.java @@ -9,26 +9,27 @@ import java.util.Map; */ public final class Variables { - private static final Map variables; + private static final NumberValue ZERO = new NumberValue(0); + private static final Map variables; static { variables = new HashMap<>(); - variables.put("PI", Math.PI); - variables.put("ПИ", Math.PI); - variables.put("E", Math.E); - variables.put("GOLDEN_RATIO", 1.618); + variables.put("PI", new NumberValue(Math.PI)); + variables.put("ПИ", new NumberValue(Math.PI)); + variables.put("E", new NumberValue(Math.E)); + variables.put("GOLDEN_RATIO", new NumberValue(1.618)); } public static boolean isExists(String key) { return variables.containsKey(key); } - public static double get(String key) { - if (!isExists(key)) return 0; + public static Value get(String key) { + if (!isExists(key)) return ZERO; return variables.get(key); } - public static void set(String key, double value) { + public static void set(String key, Value value) { variables.put(key, value); } } diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 698c165..da21488 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -36,6 +36,7 @@ public final class Lexer { final char current = peek(0); if (Character.isDigit(current)) tokenizeNumber(); else if (Character.isLetter(current)) tokenizeWord(); + else if (current == '"') tokenizeText(); else if (current == '#') { next(); tokenizeHexNumber(); @@ -95,7 +96,37 @@ public final class Lexer { buffer.append(current); current = next(); } - addToken(TokenType.WORD, buffer.toString()); + + final String word = buffer.toString(); + if (word.equals("print")) { + addToken(TokenType.PRINT); + } else { + addToken(TokenType.WORD, word); + } + } + + private void tokenizeText() { + next();// skip " + final StringBuilder buffer = new StringBuilder(); + char current = peek(0); + while (true) { + if (current == '\\') { + current = next(); + switch (current) { + case '"': current = next(); buffer.append('"'); continue; + case 'n': current = next(); buffer.append('\n'); continue; + case 't': current = next(); buffer.append('\t'); continue; + } + buffer.append('\\'); + continue; + } + if (current == '"') break; + buffer.append(current); + current = next(); + } + next(); // skip closing " + + addToken(TokenType.TEXT, buffer.toString()); } private char next() { diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index ab03480..f8406b3 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -1,10 +1,11 @@ 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.VariabletExpression; import com.annimon.ownlang.parser.ast.Expression; -import com.annimon.ownlang.parser.ast.NumberExpression; +import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.UnaryExpression; import java.util.ArrayList; @@ -37,6 +38,9 @@ public final class Parser { } private Statement statement() { + if (match(TokenType.PRINT)) { + return new PrintStatement(expression()); + } return assignmentStatement(); } @@ -107,14 +111,17 @@ public final class Parser { private Expression primary() { final Token current = get(0); if (match(TokenType.NUMBER)) { - return new NumberExpression(Double.parseDouble(current.getText())); + return new ValueExpression(Double.parseDouble(current.getText())); } if (match(TokenType.HEX_NUMBER)) { - return new NumberExpression(Long.parseLong(current.getText(), 16)); + return new ValueExpression(Long.parseLong(current.getText(), 16)); } if (match(TokenType.WORD)) { return new VariabletExpression(current.getText()); } + if (match(TokenType.TEXT)) { + return new ValueExpression(current.getText()); + } if (match(TokenType.LPAREN)) { Expression result = expression(); match(TokenType.RPAREN); diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 71f6906..80a2815 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -9,6 +9,10 @@ public enum TokenType { NUMBER, HEX_NUMBER, WORD, + TEXT, + + // keyword + PRINT, PLUS, MINUS, diff --git a/src/com/annimon/ownlang/parser/ast/AssignmentStatement.java b/src/com/annimon/ownlang/parser/ast/AssignmentStatement.java index b763512..02ecf80 100644 --- a/src/com/annimon/ownlang/parser/ast/AssignmentStatement.java +++ b/src/com/annimon/ownlang/parser/ast/AssignmentStatement.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; /** @@ -18,7 +19,7 @@ public final class AssignmentStatement implements Statement { @Override public void execute() { - final double result = expression.eval(); + final Value result = expression.eval(); Variables.set(variable, result); } diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index fe24df3..cd61a55 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -1,5 +1,9 @@ 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 @@ -16,14 +20,35 @@ public final class BinaryExpression implements Expression { } @Override - public double eval() { + public Value eval() { + final Value value1 = expr1.eval(); + final Value value2 = expr2.eval(); + if (value1 instanceof StringValue) { + final String string1 = value1.asString(); + switch (operation) { + case '*': { + final int iterations = (int) value2.asNumber(); + final StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < iterations; i++) { + buffer.append(string1); + } + return new StringValue(buffer.toString()); + } + case '+': + default: + return new StringValue(string1 + value2.asString()); + } + } + + final double number1 = value1.asNumber(); + final double number2 = value2.asNumber(); switch (operation) { - case '-': return expr1.eval() - expr2.eval(); - case '*': return expr1.eval() * expr2.eval(); - case '/': return expr1.eval() / expr2.eval(); + case '-': return new NumberValue(number1 - number2); + case '*': return new NumberValue(number1 * number2); + case '/': return new NumberValue(number1 / number2); case '+': default: - return expr1.eval() + expr2.eval(); + return new NumberValue(number1 + number2); } } diff --git a/src/com/annimon/ownlang/parser/ast/Expression.java b/src/com/annimon/ownlang/parser/ast/Expression.java index 2184c00..b6ccec3 100644 --- a/src/com/annimon/ownlang/parser/ast/Expression.java +++ b/src/com/annimon/ownlang/parser/ast/Expression.java @@ -1,10 +1,12 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON */ public interface Expression { - double eval(); + Value eval(); } diff --git a/src/com/annimon/ownlang/parser/ast/PrintStatement.java b/src/com/annimon/ownlang/parser/ast/PrintStatement.java new file mode 100644 index 0000000..c2d6d3c --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/PrintStatement.java @@ -0,0 +1,24 @@ +package com.annimon.ownlang.parser.ast; + +/** + * + * @author aNNiMON + */ +public final class PrintStatement implements Statement { + + private final Expression expression; + + public PrintStatement(Expression expression) { + this.expression = expression; + } + + @Override + public void execute() { + System.out.print(expression.eval()); + } + + @Override + public String toString() { + return "print " + expression; + } +} diff --git a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java index 86a005a..765f07f 100644 --- a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -1,5 +1,8 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON @@ -15,9 +18,9 @@ public final class UnaryExpression implements Expression { } @Override - public double eval() { + public Value eval() { switch (operation) { - case '-': return -expr1.eval(); + case '-': return new NumberValue(-expr1.eval().asNumber()); case '+': default: return expr1.eval(); diff --git a/src/com/annimon/ownlang/parser/ast/ValueExpression.java b/src/com/annimon/ownlang/parser/ast/ValueExpression.java new file mode 100644 index 0000000..24c9427 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ValueExpression.java @@ -0,0 +1,32 @@ +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 ValueExpression implements Expression { + + private final Value value; + + public ValueExpression(double value) { + this.value = new NumberValue(value); + } + + public ValueExpression(String value) { + this.value = new StringValue(value); + } + + @Override + public Value eval() { + return value; + } + + @Override + public String toString() { + return value.asString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/VariabletExpression.java b/src/com/annimon/ownlang/parser/ast/VariabletExpression.java index 96a64e9..575aac3 100644 --- a/src/com/annimon/ownlang/parser/ast/VariabletExpression.java +++ b/src/com/annimon/ownlang/parser/ast/VariabletExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; /** @@ -15,7 +16,7 @@ public final class VariabletExpression implements Expression { } @Override - public double eval() { + public Value eval() { if (!Variables.isExists(name)) throw new RuntimeException("Constant does not exists"); return Variables.get(name); }