From 3a21089d4e27793b4eb0d72b4cb9cdb20798d6a9 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 13 Jan 2019 21:52:07 +0200 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8C=20=D1=81=20Android-=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=81=D0=B8=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Converters.java | 61 +++++++++++++++++++ .../com/annimon/ownlang/lib/ValueUtils.java | 15 +++++ .../modules/functional/functional_chain.java | 10 +-- .../functional/functional_dropwhile.java | 14 +++-- .../modules/functional/functional_filter.java | 8 +-- .../modules/functional/functional_map.java | 17 ++---- .../modules/functional/functional_reduce.java | 7 +-- .../modules/functional/functional_sortby.java | 3 +- .../modules/functional/functional_stream.java | 10 +-- .../annimon/ownlang/modules/std/std_sync.java | 15 ++--- .../annimon/ownlang/modules/std/std_try.java | 8 +-- .../com/annimon/ownlang/parser/Lexer.java | 9 +-- .../com/annimon/ownlang/parser/Parser.java | 12 ++-- 13 files changed, 120 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java index 51afbc9..3c14435 100644 --- a/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -20,6 +20,10 @@ public final class Converters { int apply(); } + public interface VoidToLongFunction { + long apply(); + } + public interface VoidToFloatFunction { float apply(); } @@ -28,6 +32,10 @@ public final class Converters { double apply(); } + public interface VoidToCharSequenceFunction { + CharSequence apply(); + } + public interface VoidToStringFunction { String apply(); } @@ -44,6 +52,14 @@ public final class Converters { void apply(int i); } + public interface IntToLongFunction { + long apply(int i); + } + + public interface Int2ToVoidFunction { + void apply(int i1, int i2); + } + public interface Int4ToVoidFunction { void apply(int i1, int i2, int i3, int i4); } @@ -68,6 +84,10 @@ public final class Converters { void apply(double d1, double d2, double d3, double d4); } + public interface CharSequenceToVoidFunction { + void apply(CharSequence s); + } + public interface StringToVoidFunction { void apply(String s); } @@ -88,6 +108,10 @@ public final class Converters { return new FunctionValue(args -> NumberValue.of(f.apply())); } + public static FunctionValue voidToLong(VoidToLongFunction f) { + return new FunctionValue(args -> NumberValue.of(f.apply())); + } + public static FunctionValue voidToFloat(VoidToFloatFunction f) { return new FunctionValue(args -> NumberValue.of(f.apply())); } @@ -95,6 +119,10 @@ public final class Converters { public static FunctionValue voidToDouble(VoidToDoubleFunction f) { return new FunctionValue(args -> NumberValue.of(f.apply())); } + + public static FunctionValue voidToCharSequence(VoidToCharSequenceFunction f) { + return new FunctionValue(args -> new StringValue(f.apply().toString())); + } public static FunctionValue voidToString(VoidToStringFunction f) { return new FunctionValue(args -> new StringValue(f.apply())); @@ -131,6 +159,22 @@ public final class Converters { }); } + public static FunctionValue intToLong(IntToLongFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + return NumberValue.of(f.apply(args[0].asInt())); + }); + } + + public static FunctionValue int2ToVoid(Int2ToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(4, args.length); + f.apply(args[0].asInt(), + args[1].asInt()); + return NumberValue.ZERO; + }); + } + public static FunctionValue int4ToVoid(Int4ToVoidFunction f) { return new FunctionValue(args -> { Arguments.check(4, args.length); @@ -186,6 +230,23 @@ public final class Converters { }); } + public static FunctionValue charSequenceToVoid(CharSequenceToVoidFunction f) { + return charSequenceToVoid(f, false); + } + + public static FunctionValue charSequenceToVoid(CharSequenceToVoidFunction f, boolean emptyAsNull) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + final String text = args[0].asString(); + if (emptyAsNull && (text != null) && (text.isEmpty())) { + f.apply(null); + } else { + f.apply(text); + } + return NumberValue.ZERO; + }); + } + public static FunctionValue stringToVoid(StringToVoidFunction f) { return new FunctionValue(args -> { Arguments.check(1, args.length); diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 5d051bc..de5803d 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.lib; import com.annimon.ownlang.exceptions.TypeException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.Map; import org.json.JSONArray; @@ -111,4 +113,17 @@ public final class ValueUtils { } return ((FunctionValue) value).getValue(); } + + public static MapValue collectNumberConstants(Class clazz, Class type) { + MapValue result = new MapValue(20); + for (Field field : clazz.getDeclaredFields()) { + if (!Modifier.isStatic(field.getModifiers())) continue; + if (!field.getType().equals(type)) continue; + try { + result.set(field.getName(), NumberValue.of((T) field.get(type))); + } catch (IllegalAccessException ignore) { + } + } + return result; + } } diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java index fce2bc8..51ce031 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java @@ -1,11 +1,9 @@ package com.annimon.ownlang.modules.functional; -import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; public final class functional_chain implements Function { @@ -15,11 +13,7 @@ public final class functional_chain implements Function { Value result = args[0]; for (int i = 1; i < args.length; i += 2) { - final Value arg = args[i]; - if (arg.type() != Types.FUNCTION) { - throw new TypeException(arg.toString() + " is not a function"); - } - final Function function = ((FunctionValue) arg).getValue(); + final Function function = ValueUtils.consumeFunction(args[i], i); result = function.execute(result, args[i+1]); } return result; diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java index 5f4dc90..254720f 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java @@ -1,7 +1,13 @@ package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; public final class functional_dropwhile implements Function { @@ -11,12 +17,8 @@ public final class functional_dropwhile implements Function { if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); } - if (args[1].type() != Types.FUNCTION) { - throw new TypeException("Function expected in second argument"); - } - final Value container = args[0]; - final Function predicate = ((FunctionValue) args[1]).getValue(); + final Function predicate = ValueUtils.consumeFunction(args[1], 1); return dropWhileArray((ArrayValue) container, predicate); } diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java index c4749ce..6752e80 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java @@ -17,12 +17,8 @@ public final class functional_filter implements Function { @Override public Value execute(Value... args) { Arguments.check(2, args.length); - if (args[1].type() != Types.FUNCTION) { - throw new TypeException("Function expected in second argument"); - } - final Value container = args[0]; - final Function predicate = ((FunctionValue) args[1]).getValue(); + final Function predicate = ValueUtils.consumeFunction(args[1], 1); if (container.type() == Types.ARRAY) { return filterArray((ArrayValue) container, predicate, takeWhile); } @@ -55,4 +51,4 @@ public final class functional_filter implements Function { } return result; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java index 9016e69..5097954 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java @@ -4,10 +4,10 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.MapValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.util.Map; public final class functional_map implements Function { @@ -18,22 +18,13 @@ public final class functional_map implements Function { final Value container = args[0]; if (container.type() == Types.ARRAY) { - if (args[1].type() != Types.FUNCTION) { - throw new TypeException("Function expected in second arg"); - } - final Function mapper = ((FunctionValue) args[1]).getValue(); + final Function mapper = ValueUtils.consumeFunction(args[1], 1); return mapArray((ArrayValue) container, mapper); } if (container.type() == Types.MAP) { - if (args[1].type() != Types.FUNCTION) { - throw new TypeException("Function expected in second arg"); - } - if (args[2].type() != Types.FUNCTION) { - throw new TypeException("Function expected in third arg"); - } - final Function keyMapper = ((FunctionValue) args[1]).getValue(); - final Function valueMapper = ((FunctionValue) args[2]).getValue(); + final Function keyMapper = ValueUtils.consumeFunction(args[1], 1); + final Function valueMapper = ValueUtils.consumeFunction(args[2], 2); return mapMap((MapValue) container, keyMapper, valueMapper); } diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java index a128066..dbf429a 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -4,10 +4,10 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.MapValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.util.Map; public final class functional_reduce implements Function { @@ -16,12 +16,9 @@ public final class functional_reduce implements Function { public Value execute(Value... args) { Arguments.check(3, args.length); - if (args[2].type() != Types.FUNCTION) { - throw new TypeException("Function expected in third argument"); - } final Value container = args[0]; final Value identity = args[1]; - final Function accumulator = ((FunctionValue) args[2]).getValue(); + final Function accumulator = ValueUtils.consumeFunction(args[2], 2); if (container.type() == Types.ARRAY) { Value result = identity; final ArrayValue array = (ArrayValue) container; diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java index 5e87598..7ce988b 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java @@ -15,8 +15,9 @@ public final class functional_sortby implements Function { public Value execute(Value... args) { Arguments.check(2, args.length); if (args[0].type() != Types.ARRAY) { - throw new TypeException("Array expected in first argument"); + throw new TypeException("Array expected at first argument"); } + final Value[] elements = ((ArrayValue) args[0]).getCopyElements(); final Function function = ValueUtils.consumeFunction(args[1], 1); Arrays.sort(elements, (o1, o2) -> function.execute(o1).compareTo(function.execute(o2))); diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java index f600bb9..05d5948 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java @@ -96,10 +96,7 @@ public final class functional_stream implements Function { Arrays.sort(elements); break; case 1: - if (args[0].type() != Types.FUNCTION) { - throw new TypeException("Function expected in second argument"); - } - final Function comparator = ((FunctionValue) args[0]).getValue(); + final Function comparator = ValueUtils.consumeFunction(args[0], 0); Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); break; default: @@ -111,10 +108,7 @@ public final class functional_stream implements Function { private Value custom(Value... args) { Arguments.check(1, args.length); - if (args[0].type() != Types.FUNCTION) { - throw new TypeException("Function expected in first argument"); - } - final Function f = ((FunctionValue) args[0]).getValue(); + final Function f = ValueUtils.consumeFunction(args[0], 0); final Value result = f.execute(container); if (result.type() == Types.ARRAY) { return new StreamValue((ArrayValue) result); diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_sync.java b/src/main/java/com/annimon/ownlang/modules/std/std_sync.java index b1677a8..0cbef55 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_sync.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_sync.java @@ -1,7 +1,11 @@ package com.annimon.ownlang.modules.std; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -10,9 +14,6 @@ public final class std_sync implements Function { @Override public Value execute(Value... args) { Arguments.check(1, args.length); - if (args[0].type() != Types.FUNCTION) { - throw new TypeException(args[0].toString() + " is not a function"); - } final BlockingQueue queue = new LinkedBlockingQueue<>(2); final Function synchronizer = (sArgs) -> { @@ -23,7 +24,7 @@ public final class std_sync implements Function { } return NumberValue.ZERO; }; - final Function callback = ((FunctionValue) args[0]).getValue(); + final Function callback = ValueUtils.consumeFunction(args[0], 0); callback.execute(new FunctionValue(synchronizer)); try { @@ -34,4 +35,4 @@ public final class std_sync implements Function { } } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_try.java b/src/main/java/com/annimon/ownlang/modules/std/std_try.java index 4ae987b..fb3a453 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_try.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_try.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.modules.std; -import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; public final class std_try implements Function { @@ -8,11 +7,8 @@ public final class std_try implements Function { @Override public Value execute(Value... args) { Arguments.checkOrOr(1, 2, args.length); - if (args[0].type() != Types.FUNCTION) { - throw new TypeException(args[0].toString() + " is not a function"); - } try { - return ((FunctionValue) args[0]).getValue().execute(); + return ValueUtils.consumeFunction(args[0], 0).execute(); } catch (Exception ex) { if (args.length == 2) { switch (args[1].type()) { @@ -30,4 +26,4 @@ public final class std_try implements Function { } } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java index 2a64477..1884f92 100644 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -137,7 +137,7 @@ public final class Lexer { else if (current == '"') tokenizeText(); else if (current == '#') { next(); - tokenizeHexNumber(); + tokenizeHexNumber(1); } else if (OPERATOR_CHARS.indexOf(current) != -1) { tokenizeOperator(); @@ -155,7 +155,7 @@ public final class Lexer { if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { next(); next(); - tokenizeHexNumber(); + tokenizeHexNumber(2); return; } while (true) { @@ -170,7 +170,7 @@ public final class Lexer { addToken(TokenType.NUMBER, buffer.toString()); } - private void tokenizeHexNumber() { + private void tokenizeHexNumber(int skipped) { clearBuffer(); char current = peek(0); while (isHexNumber(current) || (current == '_')) { @@ -180,7 +180,8 @@ public final class Lexer { } current = next(); } - if (buffer.length() > 0) { + final int length = buffer.length(); + if (length > 0) { addToken(TokenType.HEX_NUMBER, buffer.toString()); } } diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 23b2a21..ce16bfa 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -315,7 +315,9 @@ public final class Parser { } if (lookMatch(0, TokenType.DOT)) { final List indices = variableSuffix(); - if (indices.isEmpty()) return expr; + if (indices == null || indices.isEmpty()) { + return expr; + } if (lookMatch(0, TokenType.LPAREN)) { // next function call @@ -452,7 +454,7 @@ public final class Parser { // x[0].prop += ... final int position = pos; final Expression targetExpr = qualifiedName(); - if (!(targetExpr instanceof Accessible)) { + if ((targetExpr == null) || !(targetExpr instanceof Accessible)) { pos = position; return null; } @@ -759,7 +761,7 @@ public final class Parser { if (!match(TokenType.WORD)) return null; final List indices = variableSuffix(); - if (indices.isEmpty()) { + if (indices == null || indices.isEmpty()) { return new VariableExpression(current.getText()); } return new ContainerAccessExpression(current.getText(), indices); @@ -768,7 +770,7 @@ public final class Parser { private List variableSuffix() { // .key1.arr1[expr1][expr2].key2 if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) { - return Collections.emptyList(); + return null; } final List indices = new ArrayList<>(); while (lookMatch(0, TokenType.DOT) || lookMatch(0, TokenType.LBRACKET)) { @@ -805,7 +807,7 @@ public final class Parser { ))); } final List indices = variableSuffix(); - if (indices.isEmpty()) { + if (indices == null || indices.isEmpty()) { return strExpr; } return new ContainerAccessExpression(strExpr, indices);