From 8a3719d67da9c3340a4323396174c1dfd19a5951 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 18 Feb 2016 12:35:20 +0200 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=87=D0=B8=D1=81=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=B5=20=D1=82=D0=B8=D0=BF=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperationIsNotSupportedException.java | 4 + src/com/annimon/ownlang/lib/ArrayValue.java | 5 + .../annimon/ownlang/lib/FunctionValue.java | 5 + src/com/annimon/ownlang/lib/MapValue.java | 5 + src/com/annimon/ownlang/lib/NumberValue.java | 69 ++- src/com/annimon/ownlang/lib/StringValue.java | 9 + src/com/annimon/ownlang/lib/Types.java | 10 + src/com/annimon/ownlang/lib/Value.java | 2 + .../annimon/ownlang/lib/modules/canvas.java | 32 +- .../annimon/ownlang/lib/modules/files.java | 22 +- .../lib/modules/functions/http_http.java | 2 +- .../lib/modules/functions/json_decode.java | 2 +- .../lib/modules/functions/json_encode.java | 2 +- .../lib/modules/functions/std_charat.java | 4 +- .../lib/modules/functions/std_indexof.java | 2 +- .../modules/functions/std_lastindexof.java | 2 +- .../lib/modules/functions/std_newarray.java | 2 +- .../lib/modules/functions/std_rand.java | 6 +- .../lib/modules/functions/std_sort.java | 2 +- .../lib/modules/functions/std_split.java | 2 +- .../lib/modules/functions/std_sprintf.java | 4 +- .../lib/modules/functions/std_substring.java | 4 +- .../lib/modules/functions/std_tochar.java | 2 +- .../annimon/ownlang/lib/modules/robot.java | 4 +- .../annimon/ownlang/lib/modules/types.java | 7 + src/com/annimon/ownlang/parser/Parser.java | 21 +- .../ownlang/parser/ast/BinaryExpression.java | 400 ++++++++++++++++-- .../parser/ast/ConditionalExpression.java | 4 +- .../parser/ast/ContainerAccessExpression.java | 6 +- .../ownlang/parser/ast/DoWhileStatement.java | 2 +- .../ownlang/parser/ast/ForStatement.java | 2 +- .../ownlang/parser/ast/IfStatement.java | 2 +- .../ownlang/parser/ast/TernaryExpression.java | 2 +- .../ownlang/parser/ast/UnaryExpression.java | 98 ++++- .../ownlang/parser/ast/ValueExpression.java | 2 +- .../ownlang/parser/ast/WhileStatement.java | 2 +- 36 files changed, 624 insertions(+), 127 deletions(-) diff --git a/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java b/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java index fffabca..6daec73 100644 --- a/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java +++ b/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java @@ -5,4 +5,8 @@ public final class OperationIsNotSupportedException extends RuntimeException { public OperationIsNotSupportedException(Object operation) { super("Operation " + operation + " is not supported"); } + + public OperationIsNotSupportedException(Object operation, String message) { + super("Operation " + operation + " is not supported " + message); + } } diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index ad593d0..594c205 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -72,6 +72,11 @@ public final class ArrayValue implements Value, Iterable { elements[index] = value; } + @Override + public int asInt() { + throw new TypeException("Cannot cast array to integer"); + } + @Override public double asNumber() { throw new TypeException("Cannot cast array to number"); diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index 1896795..2cc89e7 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -22,6 +22,11 @@ public final class FunctionValue implements Value { return Types.FUNCTION; } + @Override + public int asInt() { + throw new TypeException("Cannot cast function to integer"); + } + @Override public double asNumber() { throw new TypeException("Cannot cast function to number"); diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index d8cc3b9..d94cc11 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -44,6 +44,11 @@ public class MapValue implements Value, Iterable> { public void set(Value key, Value value) { map.put(key, value); } + + @Override + public int asInt() { + throw new TypeException("Cannot cast map to integer"); + } @Override public double asNumber() { diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/com/annimon/ownlang/lib/NumberValue.java index 3f43f76..85c07f2 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -6,6 +6,7 @@ package com.annimon.ownlang.lib; */ public final class NumberValue implements Value { + public static final NumberValue MINUS_ONE = new NumberValue(-1); public static final NumberValue ZERO = new NumberValue(0); public static final NumberValue ONE = new NumberValue(1); @@ -13,9 +14,9 @@ public final class NumberValue implements Value { return b ? ONE : ZERO; } - private final double value; + private final Number value; - public NumberValue(double value) { + public NumberValue(Number value) { this.value = value; } @@ -24,20 +25,53 @@ public final class NumberValue implements Value { return Types.NUMBER; } + public Number raw() { + return value; + } + + public boolean asBoolean() { + return value.intValue() != 0; + } + + public byte asByte() { + return value.byteValue(); + } + + public short asShort() { + return value.shortValue(); + } + + @Override + public int asInt() { + return value.intValue(); + } + + public long asLong() { + return value.longValue(); + } + + public float asFloat() { + return value.floatValue(); + } + + public double asDouble() { + return value.doubleValue(); + } + @Override public double asNumber() { - return value; + return value.doubleValue(); } @Override public String asString() { - return Double.toString(value); + return value.toString(); } @Override public int hashCode() { int hash = 3; - hash = 71 * hash + (int) (Double.doubleToLongBits(this.value) ^ (Double.doubleToLongBits(this.value) >>> 32)); + hash = 71 * hash + value.hashCode(); return hash; } @@ -47,14 +81,33 @@ public final class NumberValue implements Value { if (obj == null) return false; if (getClass() != obj.getClass()) return false; - final NumberValue other = (NumberValue) obj; - return Double.doubleToLongBits(this.value) == Double.doubleToLongBits(other.value); + final Number other = ((NumberValue) obj).value; + if (value instanceof Double || other instanceof Double) { + return Double.compare(value.doubleValue(), other.doubleValue()) == 0; + } + if (value instanceof Float || other instanceof Float) { + return Float.compare(value.floatValue(), other.floatValue()) == 0; + } + if (value instanceof Long || other instanceof Long) { + return Long.compare(value.longValue(), other.longValue()) == 0; + } + return Integer.compare(value.intValue(), other.intValue()) == 0; } @Override public int compareTo(Value o) { if (o.type() == Types.NUMBER) { - return Double.compare(value, ((NumberValue)o).value); + final Number other = ((NumberValue) o).value; + if (value instanceof Double || other instanceof Double) { + return Double.compare(value.doubleValue(), other.doubleValue()); + } + if (value instanceof Float || other instanceof Float) { + return Float.compare(value.floatValue(), other.floatValue()); + } + if (value instanceof Long || other instanceof Long) { + return Long.compare(value.longValue(), other.longValue()); + } + return Integer.compare(value.intValue(), other.intValue()); } return asString().compareTo(o.asString()); } diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index 8e820a9..fc6fa89 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -25,6 +25,15 @@ public final class StringValue implements Value { return Types.STRING; } + @Override + public int asInt() { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return 0; + } + } + @Override public double asNumber() { try { diff --git a/src/com/annimon/ownlang/lib/Types.java b/src/com/annimon/ownlang/lib/Types.java index 246b1c6..b588e52 100644 --- a/src/com/annimon/ownlang/lib/Types.java +++ b/src/com/annimon/ownlang/lib/Types.java @@ -9,4 +9,14 @@ public final class Types { ARRAY = 3, MAP = 4, FUNCTION = 5; + + private static int FIRST = OBJECT, LAST = FUNCTION; + private static final String[] NAMES = {"object", "number", "string", "array", "map", "function"}; + + public static String typeToString(int type) { + if (FIRST <= type && type <= LAST) { + return NAMES[type]; + } + return "unknown"; + } } diff --git a/src/com/annimon/ownlang/lib/Value.java b/src/com/annimon/ownlang/lib/Value.java index 832e9f7..e585313 100644 --- a/src/com/annimon/ownlang/lib/Value.java +++ b/src/com/annimon/ownlang/lib/Value.java @@ -6,6 +6,8 @@ package com.annimon.ownlang.lib; */ public interface Value extends Comparable { + int asInt(); + double asNumber(); String asString(); diff --git a/src/com/annimon/ownlang/lib/modules/canvas.java b/src/com/annimon/ownlang/lib/modules/canvas.java index b4e05c4..91d6736 100644 --- a/src/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/com/annimon/ownlang/lib/modules/canvas.java @@ -22,8 +22,6 @@ import javax.swing.JPanel; */ public final class canvas implements Module { - private static final NumberValue MINUS_ONE = new NumberValue(-1); - private static JFrame frame; private static CanvasPanel panel; private static Graphics2D graphics; @@ -55,7 +53,7 @@ public final class canvas implements Module { Variables.set("VK_FIRE", new NumberValue(KeyEvent.VK_ENTER)); Variables.set("VK_ESCAPE", new NumberValue(KeyEvent.VK_ESCAPE)); - lastKey = MINUS_ONE; + lastKey = NumberValue.MINUS_ONE; mouseHover = new ArrayValue(new Value[] { NumberValue.ZERO, NumberValue.ZERO }); } @@ -91,11 +89,7 @@ public final class canvas implements Module { private static Function intConsumer4Convert(IntConsumer4 consumer) { return args -> { if (args.length != 4) throw new ArgumentsMismatchException("Four args expected"); - int x = (int) args[0].asNumber(); - int y = (int) args[1].asNumber(); - int w = (int) args[2].asNumber(); - int h = (int) args[3].asNumber(); - consumer.accept(x, y, w, h); + consumer.accept(args[0].asInt(), args[1].asInt(), args[2].asInt(), args[3].asInt()); return NumberValue.ZERO; }; } @@ -116,7 +110,7 @@ public final class canvas implements Module { } @Override public void keyReleased(KeyEvent e) { - lastKey = MINUS_ONE; + lastKey = NumberValue.MINUS_ONE; } }); addMouseMotionListener(new MouseMotionAdapter() { @@ -147,13 +141,13 @@ public final class canvas implements Module { title = args[0].asString(); break; case 2: - width = (int) args[0].asNumber(); - height = (int) args[1].asNumber(); + width = args[0].asInt(); + height = args[1].asInt(); break; case 3: title = args[0].asString(); - width = (int) args[1].asNumber(); - height = (int) args[2].asNumber(); + width = args[1].asInt(); + height = args[2].asInt(); break; } panel = new CanvasPanel(width, height); @@ -188,8 +182,8 @@ public final class canvas implements Module { @Override public Value execute(Value... args) { if (args.length != 3) throw new ArgumentsMismatchException("Three args expected"); - int x = (int) args[1].asNumber(); - int y = (int) args[2].asNumber(); + int x = args[1].asInt(); + int y = args[2].asInt(); graphics.drawString(args[0].asString(), x, y); return NumberValue.ZERO; } @@ -219,12 +213,12 @@ public final class canvas implements Module { @Override public Value execute(Value... args) { if (args.length == 1) { - graphics.setColor(new Color((int) args[0].asNumber())); + graphics.setColor(new Color(args[0].asInt())); return NumberValue.ZERO; } - int r = (int) args[0].asNumber(); - int g = (int) args[1].asNumber(); - int b = (int) args[2].asNumber(); + int r = args[0].asInt(); + int g = args[1].asInt(); + int b = args[2].asInt(); graphics.setColor(new Color(r, g, b)); return NumberValue.ZERO; } diff --git a/src/com/annimon/ownlang/lib/modules/files.java b/src/com/annimon/ownlang/lib/modules/files.java index 9aea629..5eaa00c 100644 --- a/src/com/annimon/ownlang/lib/modules/files.java +++ b/src/com/annimon/ownlang/lib/modules/files.java @@ -69,7 +69,7 @@ public final class files implements Module { } return process(file, "r"); } catch (IOException ioe) { - return new NumberValue(-1); + return NumberValue.MINUS_ONE; } } @@ -101,7 +101,7 @@ public final class files implements Module { @Override public Value execute(Value... args) { if (args.length < 1) throw new ArgumentsMismatchException("File descriptor expected"); - final int key = (int) args[0].asNumber(); + final int key = args[0].asInt(); try { return execute(files.get(key), args); } catch (IOException ioe) { @@ -115,7 +115,7 @@ public final class files implements Module { private static class readBoolean extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return new NumberValue(fileInfo.dis.readBoolean() ? 1 : 0); + return NumberValue.fromBoolean(fileInfo.dis.readBoolean()); } } @@ -132,8 +132,8 @@ public final class files implements Module { final ArrayValue array = (ArrayValue) args[1]; int offset = 0, length = array.size(); if (args.length > 3) { - offset = (int) args[2].asNumber(); - length = (int) args[3].asNumber(); + offset = args[2].asInt(); + length = args[3].asInt(); } final byte[] buffer = new byte[length]; @@ -170,7 +170,7 @@ public final class files implements Module { private static class readChar extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return new NumberValue(fileInfo.dis.readChar()); + return new NumberValue((short)fileInfo.dis.readChar()); } } @@ -239,7 +239,7 @@ public final class files implements Module { private static class writeBoolean extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - fileInfo.dos.writeBoolean(args[1].asNumber() != 0); + fileInfo.dos.writeBoolean(args[1].asInt() != 0); return NumberValue.ONE; } } @@ -247,7 +247,7 @@ public final class files implements Module { private static class writeByte extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - fileInfo.dos.writeByte((byte) args[1].asNumber()); + fileInfo.dos.writeByte((byte) args[1].asInt()); return NumberValue.ONE; } } @@ -256,7 +256,7 @@ public final class files implements Module { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { final char ch = (args[1].type() == Types.NUMBER) - ? ((char) args[1].asNumber()) + ? ((char) args[1].asInt()) : args[1].asString().charAt(0); fileInfo.dos.writeChar(ch); return NumberValue.ONE; @@ -266,7 +266,7 @@ public final class files implements Module { private static class writeShort extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - fileInfo.dos.writeShort((short) args[1].asNumber()); + fileInfo.dos.writeShort((short) args[1].asInt()); return NumberValue.ONE; } } @@ -274,7 +274,7 @@ public final class files implements Module { private static class writeInt extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - fileInfo.dos.writeInt((int) args[1].asNumber()); + fileInfo.dos.writeInt(args[1].asInt()); return NumberValue.ONE; } } diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_http.java b/src/com/annimon/ownlang/lib/modules/functions/http_http.java index a81e73a..4e801ef 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/http_http.java +++ b/src/com/annimon/ownlang/lib/modules/functions/http_http.java @@ -141,7 +141,7 @@ public final class http_http implements Function { private RequestBody getMapRequestBody(MapValue params, MapValue options) throws UnsupportedEncodingException { final FormBody.Builder form = new FormBody.Builder(); - final boolean alreadyEncoded = (options.containsKey(ENCODED_KEY) && options.get(ENCODED_KEY).asNumber() != 0); + final boolean alreadyEncoded = (options.containsKey(ENCODED_KEY) && options.get(ENCODED_KEY).asInt() != 0); for (Map.Entry param : params) { final String name = param.getKey().asString(); final String value = param.getValue().asString(); diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java index 57be629..f9d102d 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/json_decode.java +++ b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java @@ -30,7 +30,7 @@ public final class json_decode implements Function { return new StringValue((String) obj); } if (obj instanceof Number) { - return new NumberValue(((Number) obj).doubleValue()); + return new NumberValue(((Number) obj)); } if (obj instanceof Boolean) { return NumberValue.fromBoolean((Boolean) obj); diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java index 23d80e8..bf880ca 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/json_encode.java +++ b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java @@ -26,7 +26,7 @@ public final class json_encode implements Function { case Types.MAP: return process((MapValue) val); case Types.NUMBER: - return val.asNumber(); + return ((NumberValue) val).raw(); case Types.STRING: return val.asString(); default: diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_charat.java b/src/com/annimon/ownlang/lib/modules/functions/std_charat.java index 3a24931..5ab4d9a 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_charat.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_charat.java @@ -10,8 +10,8 @@ public final class std_charat implements Function { if (args.length != 2) throw new ArgumentsMismatchException("Two arguments expected"); final String input = args[0].asString(); - final int index = (int) args[1].asNumber(); + final int index = args[1].asInt(); - return new NumberValue(input.charAt(index)); + return new NumberValue((short)input.charAt(index)); } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_indexof.java b/src/com/annimon/ownlang/lib/modules/functions/std_indexof.java index 032871a..1fcce43 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_indexof.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_indexof.java @@ -11,7 +11,7 @@ public final class std_indexof implements Function { final String input = args[0].asString(); final String what = args[1].asString(); - final int index = (args.length == 3) ? ((int) args[2].asNumber()) : 0; + final int index = (args.length == 3) ? args[2].asInt() : 0; return new NumberValue(input.indexOf(what, index)); } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java b/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java index 5b10251..14d780a 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java @@ -11,7 +11,7 @@ public final class std_lastindexof implements Function { final String input = args[0].asString(); final String what = args[1].asString(); - final int index = (args.length == 3) ? ((int) args[2].asNumber()) : 0; + final int index = (args.length == 3) ? args[2].asInt() : 0; return new NumberValue(input.lastIndexOf(what, index)); } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java b/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java index f2a1fc2..7b39f6c 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java @@ -13,7 +13,7 @@ public final class std_newarray implements Function { } private ArrayValue createArray(Value[] args, int index) { - final int size = (int) args[index].asNumber(); + final int size = args[index].asInt(); final int last = args.length - 1; ArrayValue array = new ArrayValue(size); if (index == last) { diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_rand.java b/src/com/annimon/ownlang/lib/modules/functions/std_rand.java index cc673bd..455beb3 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_rand.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_rand.java @@ -17,10 +17,10 @@ public final class std_rand implements Function { int from = 0; int to = 100; if (args.length == 1) { - to = (int) args[0].asNumber(); + to = args[0].asInt(); } else if (args.length == 2) { - from = (int) args[0].asNumber(); - to = (int) args[1].asNumber(); + from = args[0].asInt(); + to = args[1].asInt(); } return new NumberValue(RND.nextInt(to - from) + from); } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sort.java b/src/com/annimon/ownlang/lib/modules/functions/std_sort.java index edd0e40..3d44861 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_sort.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sort.java @@ -24,7 +24,7 @@ public final class std_sort implements Function { throw new TypeException("Function expected in second argument"); } final Function comparator = ((FunctionValue) args[1]).getValue(); - Arrays.sort(elements, (o1, o2) -> (int) comparator.execute(o1, o2).asNumber()); + Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); break; default: throw new ArgumentsMismatchException("Wrong number of arguments"); diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_split.java b/src/com/annimon/ownlang/lib/modules/functions/std_split.java index 13f2bde..51ccdbe 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_split.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_split.java @@ -11,7 +11,7 @@ public final class std_split implements Function { final String input = args[0].asString(); final String regex = args[1].asString(); - final int limit = (args.length == 3) ? ((int) args[2].asNumber()) : 0; + final int limit = (args.length == 3) ? args[2].asInt() : 0; final String[] parts = input.split(regex, limit); final ArrayValue result = new ArrayValue(parts.length); diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java b/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java index 1767be6..cb282e7 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java @@ -12,7 +12,9 @@ public final class std_sprintf implements Function { final String format = args[0].asString(); final Object[] values = new Object[args.length - 1]; for (int i = 1; i < args.length; i++) { - values[i - 1] = (args[i].type() == Types.NUMBER) ? args[i].asNumber() : args[i].asString(); + values[i - 1] = (args[i].type() == Types.NUMBER) + ? ((NumberValue) args[i]).raw() + : args[i].asString(); } return new StringValue(String.format(format, values)); } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_substring.java b/src/com/annimon/ownlang/lib/modules/functions/std_substring.java index 22e697a..62bb88a 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_substring.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_substring.java @@ -10,13 +10,13 @@ public final class std_substring implements Function { if (args.length < 2 || args.length > 3) throw new ArgumentsMismatchException("Two or three arguments expected"); final String input = args[0].asString(); - final int startIndex = (int) args[1].asNumber(); + final int startIndex = args[1].asInt(); String result; if (args.length == 2) { result = input.substring(startIndex); } else { - final int endIndex = (int) args[2].asNumber(); + final int endIndex = args[2].asInt(); result = input.substring(startIndex, endIndex); } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java b/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java index 00b5c25..6cf4828 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java @@ -9,6 +9,6 @@ public final class std_tochar implements Function { public Value execute(Value... args) { if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); - return new StringValue(String.valueOf((char) args[0].asNumber())); + return new StringValue(String.valueOf((char) args[0].asInt())); } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/robot.java b/src/com/annimon/ownlang/lib/modules/robot.java index 7a91a03..4372729 100644 --- a/src/com/annimon/ownlang/lib/modules/robot.java +++ b/src/com/annimon/ownlang/lib/modules/robot.java @@ -45,7 +45,7 @@ public final class robot implements Module { Functions.set("mouseMove", (args) -> { if (args.length != 2) throw new ArgumentsMismatchException("Two arguments expected"); try { - awtRobot.mouseMove((int) args[0].asNumber(), (int) args[1].asNumber()); + awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); } catch (IllegalArgumentException iae) { } return NumberValue.ZERO; }); @@ -89,7 +89,7 @@ public final class robot implements Module { return args -> { if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); try { - consumer.accept((int) args[0].asNumber()); + consumer.accept(args[0].asInt()); } catch (IllegalArgumentException iae) { } return NumberValue.ZERO; }; diff --git a/src/com/annimon/ownlang/lib/modules/types.java b/src/com/annimon/ownlang/lib/modules/types.java index e82d2a2..4b390a5 100644 --- a/src/com/annimon/ownlang/lib/modules/types.java +++ b/src/com/annimon/ownlang/lib/modules/types.java @@ -20,5 +20,12 @@ public final class types implements Module { Functions.set("typeof", args -> new NumberValue(args[0].type())); Functions.set("string", args -> new StringValue(args[0].asString())); Functions.set("number", args -> new NumberValue(args[0].asNumber())); + + Functions.set("byte", args -> new NumberValue((byte)args[0].asInt())); + Functions.set("short", args -> new NumberValue((short)args[0].asInt())); + Functions.set("int", args -> new NumberValue(args[0].asInt())); + Functions.set("long", args -> new NumberValue((long)args[0].asNumber())); + Functions.set("float", args -> new NumberValue((float)args[0].asNumber())); + Functions.set("double", args -> new NumberValue(args[0].asNumber())); } } diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 47d2245..89011f4 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -327,12 +327,12 @@ public final class Parser { if (match(TokenType.NUMBER)) { // case 0.5: pattern = new MatchExpression.ConstantPattern( - new NumberValue(Double.parseDouble(current.getText())) + new NumberValue(createNumber(current.getText(), 10)) ); } else if (match(TokenType.HEX_NUMBER)) { // case #FF: pattern = new MatchExpression.ConstantPattern( - new NumberValue(Long.parseLong(current.getText(), 16)) + new NumberValue(createNumber(current.getText(), 16)) ); } else if (match(TokenType.TEXT)) { // case "text": @@ -703,10 +703,10 @@ public final class Parser { private Expression value() { final Token current = get(0); if (match(TokenType.NUMBER)) { - return new ValueExpression(Double.parseDouble(current.getText())); + return new ValueExpression(createNumber(current.getText(), 10)); } if (match(TokenType.HEX_NUMBER)) { - return new ValueExpression(Long.parseLong(current.getText(), 16)); + return new ValueExpression(createNumber(current.getText(), 16)); } if (match(TokenType.TEXT)) { return new ValueExpression(current.getText()); @@ -714,6 +714,19 @@ public final class Parser { throw new ParseException("Unknown expression: " + current); } + private Number createNumber(String text, int radix) { + // Double + if (text.contains(".")) { + return Double.parseDouble(text); + } + // Integer + try { + return Integer.parseInt(text, radix); + } catch (NumberFormatException nfe) { + return Long.parseLong(text, radix); + } + } + private Token consume(TokenType type) { final Token current = get(0); if (type != current.getType()) throw new ParseException("Token " + current + " doesn't match " + type); diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index e870ebb..f55369e 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -54,70 +54,384 @@ public final class BinaryExpression implements Expression { public Value eval() { final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); - - switch (value1.type()) { - case Types.STRING: - return eval((StringValue) value1, value2); - case Types.ARRAY: - return eval((ArrayValue) value1, value2); - case Types.NUMBER: + switch (operation) { + case ADD: return add(value1, value2); + case SUBTRACT: return subtract(value1, value2); + case MULTIPLY: return multiply(value1, value2); + case DIVIDE: return divide(value1, value2); + case REMAINDER: return remainder(value1, value2); + case PUSH: return push(value1, value2); + case AND: return and(value1, value2); + case OR: return or(value1, value2); + case XOR: return xor(value1, value2); + case LSHIFT: return lshift(value1, value2); + case RSHIFT: return rshift(value1, value2); + case URSHIFT: return urshift(value1, value2); default: - return eval(value1, value2); + throw new OperationIsNotSupportedException(operation); } } - private Value eval(StringValue value1, Value value2) { - final String string1 = value1.asString(); - switch (operation) { - case MULTIPLY: { - final int iterations = (int) value2.asNumber(); + private Value add(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return add((NumberValue) value1, value2); + case Types.ARRAY: return ArrayValue.add((ArrayValue) value1, value2); + case Types.MAP: /* TODO: merge maps */ + case Types.FUNCTION: /* TODO: combining functions */ + case Types.STRING: + default: + // Concatenation strings + return new StringValue(value1.asString() + value2.asString()); + } + } + + private Value add(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 + number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Double || number2 instanceof Double) { + return new NumberValue(number1.doubleValue() + number2.doubleValue()); + } + if (number1 instanceof Float || number2 instanceof Float) { + return new NumberValue(number1.floatValue() + number2.floatValue()); + } + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() + number2.longValue()); + } + return new NumberValue(number1.intValue() + number2.intValue()); + } + // number1 + other + if (number1 instanceof Double) { + return new NumberValue(number1.doubleValue() + value2.asNumber()); + } + if (number1 instanceof Float) { + return new NumberValue(number1.floatValue() + value2.asNumber()); + } + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() + value2.asInt()); + } + return new NumberValue(number1.intValue() + value2.asInt()); + } + + private Value subtract(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return subtract((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value subtract(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 - number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Double || number2 instanceof Double) { + return new NumberValue(number1.doubleValue() - number2.doubleValue()); + } + if (number1 instanceof Float || number2 instanceof Float) { + return new NumberValue(number1.floatValue() - number2.floatValue()); + } + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() - number2.longValue()); + } + return new NumberValue(number1.intValue() - number2.intValue()); + } + // number1 - other + if (number1 instanceof Double) { + return new NumberValue(number1.doubleValue() - value2.asNumber()); + } + if (number1 instanceof Float) { + return new NumberValue(number1.floatValue() - value2.asNumber()); + } + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() - value2.asInt()); + } + return new NumberValue(number1.intValue() - value2.asInt()); + } + + private Value multiply(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return multiply((NumberValue) value1, value2); + case Types.STRING: { + final String string1 = value1.asString(); + final int iterations = value2.asInt(); final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < iterations; i++) { buffer.append(string1); } return new StringValue(buffer.toString()); } - case ADD: default: - return new StringValue(string1 + value2.asString()); + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); } } - private Value eval(ArrayValue value1, Value value2) { - switch (operation) { - case LSHIFT: + private Value multiply(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 * number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Double || number2 instanceof Double) { + return new NumberValue(number1.doubleValue() * number2.doubleValue()); + } + if (number1 instanceof Float || number2 instanceof Float) { + return new NumberValue(number1.floatValue() * number2.floatValue()); + } + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() * number2.longValue()); + } + return new NumberValue(number1.intValue() * number2.intValue()); + } + // number1 * other + if (number1 instanceof Double) { + return new NumberValue(number1.doubleValue() * value2.asNumber()); + } + if (number1 instanceof Float) { + return new NumberValue(number1.floatValue() * value2.asNumber()); + } + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() * value2.asInt()); + } + return new NumberValue(number1.intValue() * value2.asInt()); + } + + private Value divide(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return divide((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value divide(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 / number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Double || number2 instanceof Double) { + return new NumberValue(number1.doubleValue() / number2.doubleValue()); + } + if (number1 instanceof Float || number2 instanceof Float) { + return new NumberValue(number1.floatValue() / number2.floatValue()); + } + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() / number2.longValue()); + } + return new NumberValue(number1.intValue() / number2.intValue()); + } + // number1 / other + if (number1 instanceof Double) { + return new NumberValue(number1.doubleValue() / value2.asNumber()); + } + if (number1 instanceof Float) { + return new NumberValue(number1.floatValue() / value2.asNumber()); + } + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() / value2.asInt()); + } + return new NumberValue(number1.intValue() / value2.asInt()); + } + + private Value remainder(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return remainder((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value remainder(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 % number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Double || number2 instanceof Double) { + return new NumberValue(number1.doubleValue() % number2.doubleValue()); + } + if (number1 instanceof Float || number2 instanceof Float) { + return new NumberValue(number1.floatValue() % number2.floatValue()); + } + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() % number2.longValue()); + } + return new NumberValue(number1.intValue() % number2.intValue()); + } + // number1 % other + if (number1 instanceof Double) { + return new NumberValue(number1.doubleValue() % value2.asNumber()); + } + if (number1 instanceof Float) { + return new NumberValue(number1.floatValue() % value2.asNumber()); + } + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() % value2.asInt()); + } + return new NumberValue(number1.intValue() % value2.asInt()); + } + + private Value push(Value value1, Value value2) { + switch (value1.type()) { + case Types.ARRAY: return ArrayValue.add((ArrayValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value and(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return and((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value and(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 & number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() & number2.longValue()); + } + return new NumberValue(number1.intValue() & number2.intValue()); + } + // number1 & other + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() & value2.asInt()); + } + return new NumberValue(number1.intValue() & value2.asInt()); + } + + private Value or(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return or((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value or(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 | number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() | number2.longValue()); + } + return new NumberValue(number1.intValue() | number2.intValue()); + } + // number1 | other + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() | value2.asInt()); + } + return new NumberValue(number1.intValue() | value2.asInt()); + } + + private Value xor(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return xor((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value xor(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 ^ number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() ^ number2.longValue()); + } + return new NumberValue(number1.intValue() ^ number2.intValue()); + } + // number1 ^ other + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() ^ value2.asInt()); + } + return new NumberValue(number1.intValue() ^ value2.asInt()); + } + + private Value lshift(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return lshift((NumberValue) value1, value2); + case Types.ARRAY: { if (value2.type() != Types.ARRAY) throw new TypeException("Cannot merge non array value to array"); - return ArrayValue.merge(value1, (ArrayValue) value2); - case PUSH: + return ArrayValue.merge((ArrayValue) value1, (ArrayValue) value2); + } default: - return ArrayValue.add(value1, value2); + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); } } - private Value eval(Value value1, Value value2) { - final double number1 = value1.asNumber(); - final double number2 = value2.asNumber(); - double result; - switch (operation) { - case ADD: result = number1 + number2; break; - case SUBTRACT: result = number1 - number2; break; - case MULTIPLY: result = number1 * number2; break; - case DIVIDE: result = number1 / number2; break; - case REMAINDER: result = number1 % number2; break; - - // Bitwise - case AND: result = (int)number1 & (int)number2; break; - case XOR: result = (int)number1 ^ (int)number2; break; - case OR: result = (int)number1 | (int)number2; break; - case LSHIFT: result = (int)number1 << (int)number2; break; - case RSHIFT: result = (int)number1 >> (int)number2; break; - case URSHIFT: result = (int)number1 >>> (int)number2; break; - - default: - throw new OperationIsNotSupportedException(operation); + private Value lshift(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 << number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() << number2.longValue()); + } + return new NumberValue(number1.intValue() << number2.intValue()); } - return new NumberValue(result); + // number1 << other + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() << value2.asInt()); + } + return new NumberValue(number1.intValue() << value2.asInt()); + } + + private Value rshift(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return rshift((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value rshift(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 >> number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() >> number2.longValue()); + } + return new NumberValue(number1.intValue() >> number2.intValue()); + } + // number1 >> other + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() >> value2.asInt()); + } + return new NumberValue(number1.intValue() >> value2.asInt()); + } + + private Value urshift(Value value1, Value value2) { + switch (value1.type()) { + case Types.NUMBER: return urshift((NumberValue) value1, value2); + default: + throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + } + } + + private Value urshift(NumberValue value1, Value value2) { + final Number number1 = value1.raw(); + if (value2.type() == Types.NUMBER) { + // number1 >>> number2 + final Number number2 = ((NumberValue) value2).raw(); + if (number1 instanceof Long || number2 instanceof Long) { + return new NumberValue(number1.longValue() >>> number2.longValue()); + } + return new NumberValue(number1.intValue() >>> number2.intValue()); + } + // number1 >>> other + if (number1 instanceof Long) { + return new NumberValue(number1.longValue() >>> value2.asInt()); + } + return new NumberValue(number1.intValue() >>> value2.asInt()); } @Override diff --git a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java index 569df03..87668e8 100644 --- a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -48,9 +48,9 @@ public final class ConditionalExpression implements Expression { final Value value1 = expr1.eval(); switch (operation) { case AND: return NumberValue.fromBoolean( - (value1.asNumber() != 0) && (expr2.eval().asNumber() != 0) ); + (value1.asInt() != 0) && (expr2.eval().asInt() != 0) ); case OR: return NumberValue.fromBoolean( - (value1.asNumber() != 0) || (expr2.eval().asNumber() != 0) ); + (value1.asInt() != 0) || (expr2.eval().asInt() != 0) ); } diff --git a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index f3c0f29..0f06adf 100644 --- a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -29,7 +29,7 @@ public final class ContainerAccessExpression implements Expression, Accessible { final Value lastIndex = lastIndex(); switch (container.type()) { case Types.ARRAY: - final int arrayIndex = (int) lastIndex.asNumber(); + final int arrayIndex = lastIndex.asInt(); return ((ArrayValue) container).get(arrayIndex); case Types.MAP: @@ -46,7 +46,7 @@ public final class ContainerAccessExpression implements Expression, Accessible { final Value lastIndex = lastIndex(); switch (container.type()) { case Types.ARRAY: - final int arrayIndex = (int) lastIndex.asNumber(); + final int arrayIndex = lastIndex.asInt(); ((ArrayValue) container).set(arrayIndex, value); return value; @@ -66,7 +66,7 @@ public final class ContainerAccessExpression implements Expression, Accessible { final Value index = index(i); switch (container.type()) { case Types.ARRAY: - final int arrayIndex = (int) index.asNumber(); + final int arrayIndex = index.asInt(); container = ((ArrayValue) container).get(arrayIndex); break; diff --git a/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java b/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java index 783098c..d3ee971 100644 --- a/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java +++ b/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java @@ -25,7 +25,7 @@ public final class DoWhileStatement implements Statement { // continue; } } - while (condition.eval().asNumber() != 0); + while (condition.eval().asInt() != 0); } @Override diff --git a/src/com/annimon/ownlang/parser/ast/ForStatement.java b/src/com/annimon/ownlang/parser/ast/ForStatement.java index cd3d03f..eaf7df6 100644 --- a/src/com/annimon/ownlang/parser/ast/ForStatement.java +++ b/src/com/annimon/ownlang/parser/ast/ForStatement.java @@ -20,7 +20,7 @@ public final class ForStatement implements Statement { @Override public void execute() { - for (initialization.execute(); termination.eval().asNumber() != 0; increment.execute()) { + for (initialization.execute(); termination.eval().asInt() != 0; increment.execute()) { try { statement.execute(); } catch (BreakStatement bs) { diff --git a/src/com/annimon/ownlang/parser/ast/IfStatement.java b/src/com/annimon/ownlang/parser/ast/IfStatement.java index 0ba8e23..64c8700 100644 --- a/src/com/annimon/ownlang/parser/ast/IfStatement.java +++ b/src/com/annimon/ownlang/parser/ast/IfStatement.java @@ -17,7 +17,7 @@ public final class IfStatement implements Statement { @Override public void execute() { - final double result = expression.eval().asNumber(); + final int result = expression.eval().asInt(); if (result != 0) { ifStatement.execute(); } else if (elseStatement != null) { diff --git a/src/com/annimon/ownlang/parser/ast/TernaryExpression.java b/src/com/annimon/ownlang/parser/ast/TernaryExpression.java index 6d7bcce..5fec52b 100644 --- a/src/com/annimon/ownlang/parser/ast/TernaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/TernaryExpression.java @@ -19,7 +19,7 @@ public final class TernaryExpression implements Expression { @Override public Value eval() { - if (condition.eval().asNumber() != 0) { + if (condition.eval().asInt() != 0) { return trueExpr.eval(); } else { return falseExpr.eval(); diff --git a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java index 1d688bd..75b1f79 100644 --- a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -2,6 +2,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; /** @@ -52,38 +54,104 @@ public final class UnaryExpression implements Expression, Statement { switch (operation) { case INCREMENT_PREFIX: { if (expr1 instanceof Accessible) { - return ((Accessible) expr1).set(new NumberValue(value.asNumber() + 1)); + return ((Accessible) expr1).set(increment(value)); } - return new NumberValue(value.asNumber() + 1); + return increment(value); } case DECREMENT_PREFIX: { if (expr1 instanceof Accessible) { - return ((Accessible) expr1).set(new NumberValue(value.asNumber() - 1)); + return ((Accessible) expr1).set(decrement(value)); } - return new NumberValue(value.asNumber() - 1); + return decrement(value); } case INCREMENT_POSTFIX: { if (expr1 instanceof Accessible) { - ((Accessible) expr1).set(new NumberValue(value.asNumber() + 1)); + ((Accessible) expr1).set(increment(value)); return value; } - return new NumberValue(value.asNumber() + 1); + return increment(value); } case DECREMENT_POSTFIX: { if (expr1 instanceof Accessible) { - ((Accessible) expr1).set(new NumberValue(value.asNumber() - 1)); + ((Accessible) expr1).set(decrement(value)); return value; } - return new NumberValue(value.asNumber() - 1); + return decrement(value); } - case NEGATE: return new NumberValue(-value.asNumber()); - case COMPLEMENT: return new NumberValue(~(int)value.asNumber()); - case NOT: return new NumberValue(value.asNumber() != 0 ? 0 : 1); + case NEGATE: return negate(value); + case COMPLEMENT: return complement(value); + case NOT: return not(value); default: throw new OperationIsNotSupportedException(operation); } } + private Value increment(Value value) { + if (value.type() == Types.NUMBER) { + final Number number = ((NumberValue) value).raw(); + if (number instanceof Double) { + return new NumberValue(number.doubleValue() + 1); + } + if (number instanceof Float) { + return new NumberValue(number.floatValue() + 1); + } + if (number instanceof Long) { + return new NumberValue(number.longValue() + 1); + } + } + return new NumberValue(value.asInt() + 1); + } + + private Value decrement(Value value) { + if (value.type() == Types.NUMBER) { + final Number number = ((NumberValue) value).raw(); + if (number instanceof Double) { + return new NumberValue(number.doubleValue() - 1); + } + if (number instanceof Float) { + return new NumberValue(number.floatValue() - 1); + } + if (number instanceof Long) { + return new NumberValue(number.longValue() - 1); + } + } + return new NumberValue(value.asInt() - 1); + } + + private Value negate(Value value) { + if (value.type() == Types.STRING) { + final StringBuilder sb = new StringBuilder(value.asString()); + return new StringValue(sb.reverse().toString()); + } + if (value.type() == Types.NUMBER) { + final Number number = ((NumberValue) value).raw(); + if (number instanceof Double) { + return new NumberValue(-number.doubleValue()); + } + if (number instanceof Float) { + return new NumberValue(-number.floatValue()); + } + if (number instanceof Long) { + return new NumberValue(-number.longValue()); + } + } + return new NumberValue(-value.asInt()); + } + + private Value complement(Value value) { + if (value.type() == Types.NUMBER) { + final Number number = ((NumberValue) value).raw(); + if (number instanceof Long) { + return new NumberValue(~number.longValue()); + } + } + return new NumberValue(~value.asInt()); + } + + private Value not(Value value) { + return NumberValue.fromBoolean(value.asInt() == 0); + } + @Override public void accept(Visitor visitor) { visitor.visit(this); @@ -91,6 +159,12 @@ public final class UnaryExpression implements Expression, Statement { @Override public String toString() { - return String.format("%s %s", operation, expr1); + switch (operation) { + case INCREMENT_POSTFIX: + case DECREMENT_POSTFIX: + return String.format("%s %s", expr1, operation); + default: + return String.format("%s %s", operation, expr1); + } } } diff --git a/src/com/annimon/ownlang/parser/ast/ValueExpression.java b/src/com/annimon/ownlang/parser/ast/ValueExpression.java index e7f894e..1dc6477 100644 --- a/src/com/annimon/ownlang/parser/ast/ValueExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ValueExpression.java @@ -14,7 +14,7 @@ public final class ValueExpression implements Expression { public final Value value; - public ValueExpression(double value) { + public ValueExpression(Number value) { this.value = new NumberValue(value); } diff --git a/src/com/annimon/ownlang/parser/ast/WhileStatement.java b/src/com/annimon/ownlang/parser/ast/WhileStatement.java index 4b43e61..4ae348c 100644 --- a/src/com/annimon/ownlang/parser/ast/WhileStatement.java +++ b/src/com/annimon/ownlang/parser/ast/WhileStatement.java @@ -16,7 +16,7 @@ public final class WhileStatement implements Statement { @Override public void execute() { - while (condition.eval().asNumber() != 0) { + while (condition.eval().asInt() != 0) { try { statement.execute(); } catch (BreakStatement bs) {